fuser/
request.rs

1//! Filesystem operation request
2//!
3//! A request represents information about a filesystem operation the kernel driver wants us to
4//! perform.
5//!
6//! TODO: This module is meant to go away soon in favor of `ll::Request`.
7
8use crate::ll::{Errno, Response, fuse_abi as abi};
9use log::{debug, error, warn};
10use std::convert::TryFrom;
11#[cfg(feature = "abi-7-28")]
12use std::convert::TryInto;
13use std::path::Path;
14use std::sync::atomic::Ordering;
15
16use crate::Filesystem;
17use crate::PollHandle;
18use crate::channel::ChannelSender;
19use crate::ll::Request as _;
20#[cfg(feature = "abi-7-21")]
21use crate::reply::ReplyDirectoryPlus;
22use crate::reply::{Reply, ReplyDirectory, ReplySender};
23use crate::session::{Session, SessionACL};
24use crate::{KernelConfig, ll};
25
26/// Request data structure
27#[derive(Debug)]
28pub struct Request<'a> {
29    /// Channel sender for sending the reply
30    ch: ChannelSender,
31    /// Request raw data
32    #[allow(unused)]
33    data: &'a [u8],
34    /// Parsed request
35    request: ll::AnyRequest<'a>,
36}
37
38impl<'a> Request<'a> {
39    /// Create a new request from the given data
40    pub(crate) fn new(ch: ChannelSender, data: &'a [u8]) -> Option<Request<'a>> {
41        let request = match ll::AnyRequest::try_from(data) {
42            Ok(request) => request,
43            Err(err) => {
44                error!("{err}");
45                return None;
46            }
47        };
48
49        Some(Self { ch, data, request })
50    }
51
52    /// Dispatch request to the given filesystem.
53    /// This calls the appropriate filesystem operation method for the
54    /// request and sends back the returned reply to the kernel
55    pub(crate) fn dispatch<FS: Filesystem>(&self, se: &Session<FS>) {
56        debug!("{}", self.request);
57        let unique = self.request.unique();
58
59        let res = match self.dispatch_req(se) {
60            Ok(Some(resp)) => resp,
61            Ok(None) => return,
62            Err(errno) => self.request.reply_err(errno),
63        }
64        .with_iovec(unique, |iov| self.ch.send(iov));
65
66        if let Err(err) = res {
67            warn!("Request {unique:?}: Failed to send reply: {err}");
68        }
69    }
70
71    fn dispatch_req<FS: Filesystem>(
72        &self,
73        se: &Session<FS>,
74    ) -> Result<Option<Response<'_>>, Errno> {
75        let op = self.request.operation().map_err(|_| Errno::ENOSYS)?;
76        // Implement allow_root & access check for auto_unmount
77        if (se.allowed == SessionACL::RootAndOwner
78            && self.request.uid() != se.session_owner
79            && self.request.uid() != 0)
80            || (se.allowed == SessionACL::Owner && self.request.uid() != se.session_owner)
81        {
82            #[cfg(feature = "abi-7-21")]
83            {
84                match op {
85                    // Only allow operations that the kernel may issue without a uid set
86                    ll::Operation::Init(_)
87                    | ll::Operation::Destroy(_)
88                    | ll::Operation::Read(_)
89                    | ll::Operation::ReadDir(_)
90                    | ll::Operation::ReadDirPlus(_)
91                    | ll::Operation::BatchForget(_)
92                    | ll::Operation::Forget(_)
93                    | ll::Operation::Write(_)
94                    | ll::Operation::FSync(_)
95                    | ll::Operation::FSyncDir(_)
96                    | ll::Operation::Release(_)
97                    | ll::Operation::ReleaseDir(_) => {}
98                    _ => {
99                        return Err(Errno::EACCES);
100                    }
101                }
102            }
103            #[cfg(not(feature = "abi-7-21"))]
104            {
105                match op {
106                    // Only allow operations that the kernel may issue without a uid set
107                    ll::Operation::Init(_)
108                    | ll::Operation::Destroy(_)
109                    | ll::Operation::Read(_)
110                    | ll::Operation::ReadDir(_)
111                    | ll::Operation::BatchForget(_)
112                    | ll::Operation::Forget(_)
113                    | ll::Operation::Write(_)
114                    | ll::Operation::FSync(_)
115                    | ll::Operation::FSyncDir(_)
116                    | ll::Operation::Release(_)
117                    | ll::Operation::ReleaseDir(_) => {}
118                    _ => {
119                        return Err(Errno::EACCES);
120                    }
121                }
122            }
123        }
124        match op {
125            // Filesystem initialization
126            ll::Operation::Init(x) => {
127                // We don't support ABI versions before 7.6
128                let v = x.version();
129                if v < ll::Version(7, 6) {
130                    error!("Unsupported FUSE ABI version {v}");
131                    return Err(Errno::EPROTO);
132                }
133                // Remember ABI version supported by kernel
134                se.proto_major.store(v.major(), Ordering::SeqCst);
135                se.proto_minor.store(v.minor(), Ordering::SeqCst);
136
137                let mut config = KernelConfig::new(x.capabilities(), x.max_readahead());
138                // Call filesystem init method and give it a chance to return an error
139                se.filesystem
140                    .init(self, &mut config)
141                    .map_err(Errno::from_i32)?;
142
143                // Reply with our desired version and settings. If the kernel supports a
144                // larger major version, it'll re-send a matching init message. If it
145                // supports only lower major versions, we replied with an error above.
146                debug!(
147                    "INIT response: ABI {}.{}, flags {:#x}, max readahead {}, max write {}",
148                    abi::FUSE_KERNEL_VERSION,
149                    abi::FUSE_KERNEL_MINOR_VERSION,
150                    x.capabilities() & config.requested,
151                    config.max_readahead,
152                    config.max_write
153                );
154                se.initialized.store(true, Ordering::SeqCst);
155                return Ok(Some(x.reply(&config)));
156            }
157            // Any operation is invalid before initialization
158            _ if !se.initialized.load(Ordering::SeqCst) => {
159                warn!("Ignoring FUSE operation before init: {}", self.request);
160                return Err(Errno::EIO);
161            }
162            // Filesystem destroyed
163            ll::Operation::Destroy(x) => {
164                if !se.destroyed.swap(true, Ordering::SeqCst) {
165                    se.filesystem.destroy();
166                }
167                return Ok(Some(x.reply()));
168            }
169            // Any operation is invalid after destroy
170            _ if se.destroyed.load(Ordering::SeqCst) => {
171                warn!("Ignoring FUSE operation after destroy: {}", self.request);
172                return Err(Errno::EIO);
173            }
174
175            ll::Operation::Interrupt(_) => {
176                // TODO: handle FUSE_INTERRUPT
177                return Err(Errno::ENOSYS);
178            }
179
180            ll::Operation::Lookup(x) => {
181                se.filesystem.lookup(
182                    self,
183                    self.request.nodeid().into(),
184                    x.name().as_ref(),
185                    self.reply(),
186                );
187            }
188            ll::Operation::Forget(x) => {
189                se.filesystem
190                    .forget(self, self.request.nodeid().into(), x.nlookup()); // no reply
191            }
192            ll::Operation::GetAttr(_attr) => {
193                se.filesystem.getattr(
194                    self,
195                    self.request.nodeid().into(),
196                    _attr.file_handle().map(|fh| fh.into()),
197                    self.reply(),
198                );
199            }
200            ll::Operation::SetAttr(x) => {
201                se.filesystem.setattr(
202                    self,
203                    self.request.nodeid().into(),
204                    x.mode(),
205                    x.uid(),
206                    x.gid(),
207                    x.size(),
208                    x.atime(),
209                    x.mtime(),
210                    x.ctime(),
211                    x.file_handle().map(|fh| fh.into()),
212                    x.crtime(),
213                    x.chgtime(),
214                    x.bkuptime(),
215                    x.flags(),
216                    self.reply(),
217                );
218            }
219            ll::Operation::ReadLink(_) => {
220                se.filesystem
221                    .readlink(self, self.request.nodeid().into(), self.reply());
222            }
223            ll::Operation::MkNod(x) => {
224                se.filesystem.mknod(
225                    self,
226                    self.request.nodeid().into(),
227                    x.name().as_ref(),
228                    x.mode(),
229                    x.umask(),
230                    x.rdev(),
231                    self.reply(),
232                );
233            }
234            ll::Operation::MkDir(x) => {
235                se.filesystem.mkdir(
236                    self,
237                    self.request.nodeid().into(),
238                    x.name().as_ref(),
239                    x.mode(),
240                    x.umask(),
241                    self.reply(),
242                );
243            }
244            ll::Operation::Unlink(x) => {
245                se.filesystem.unlink(
246                    self,
247                    self.request.nodeid().into(),
248                    x.name().as_ref(),
249                    self.reply(),
250                );
251            }
252            ll::Operation::RmDir(x) => {
253                se.filesystem.rmdir(
254                    self,
255                    self.request.nodeid().into(),
256                    x.name().as_ref(),
257                    self.reply(),
258                );
259            }
260            ll::Operation::SymLink(x) => {
261                se.filesystem.symlink(
262                    self,
263                    self.request.nodeid().into(),
264                    x.link_name().as_ref(),
265                    Path::new(x.target()),
266                    self.reply(),
267                );
268            }
269            ll::Operation::Rename(x) => {
270                se.filesystem.rename(
271                    self,
272                    self.request.nodeid().into(),
273                    x.src().name.as_ref(),
274                    x.dest().dir.into(),
275                    x.dest().name.as_ref(),
276                    0,
277                    self.reply(),
278                );
279            }
280            ll::Operation::Link(x) => {
281                se.filesystem.link(
282                    self,
283                    x.inode_no().into(),
284                    self.request.nodeid().into(),
285                    x.dest().name.as_ref(),
286                    self.reply(),
287                );
288            }
289            ll::Operation::Open(x) => {
290                se.filesystem
291                    .open(self, self.request.nodeid().into(), x.flags(), self.reply());
292            }
293            ll::Operation::Read(x) => {
294                se.filesystem.read(
295                    self,
296                    self.request.nodeid().into(),
297                    x.file_handle().into(),
298                    x.offset(),
299                    x.size(),
300                    x.flags(),
301                    x.lock_owner().map(|l| l.into()),
302                    self.reply(),
303                );
304            }
305            ll::Operation::Write(x) => {
306                se.filesystem.write(
307                    self,
308                    self.request.nodeid().into(),
309                    x.file_handle().into(),
310                    x.offset(),
311                    x.data(),
312                    x.write_flags(),
313                    x.flags(),
314                    x.lock_owner().map(|l| l.into()),
315                    self.reply(),
316                );
317            }
318            ll::Operation::Flush(x) => {
319                se.filesystem.flush(
320                    self,
321                    self.request.nodeid().into(),
322                    x.file_handle().into(),
323                    x.lock_owner().into(),
324                    self.reply(),
325                );
326            }
327            ll::Operation::Release(x) => {
328                se.filesystem.release(
329                    self,
330                    self.request.nodeid().into(),
331                    x.file_handle().into(),
332                    x.flags(),
333                    x.lock_owner().map(|x| x.into()),
334                    x.flush(),
335                    self.reply(),
336                );
337            }
338            ll::Operation::FSync(x) => {
339                se.filesystem.fsync(
340                    self,
341                    self.request.nodeid().into(),
342                    x.file_handle().into(),
343                    x.fdatasync(),
344                    self.reply(),
345                );
346            }
347            ll::Operation::OpenDir(x) => {
348                se.filesystem
349                    .opendir(self, self.request.nodeid().into(), x.flags(), self.reply());
350            }
351            ll::Operation::ReadDir(x) => {
352                se.filesystem.readdir(
353                    self,
354                    self.request.nodeid().into(),
355                    x.file_handle().into(),
356                    x.offset(),
357                    ReplyDirectory::new(
358                        self.request.unique().into(),
359                        self.ch.clone(),
360                        x.size() as usize,
361                    ),
362                );
363            }
364            ll::Operation::ReleaseDir(x) => {
365                se.filesystem.releasedir(
366                    self,
367                    self.request.nodeid().into(),
368                    x.file_handle().into(),
369                    x.flags(),
370                    self.reply(),
371                );
372            }
373            ll::Operation::FSyncDir(x) => {
374                se.filesystem.fsyncdir(
375                    self,
376                    self.request.nodeid().into(),
377                    x.file_handle().into(),
378                    x.fdatasync(),
379                    self.reply(),
380                );
381            }
382            ll::Operation::StatFs(_) => {
383                se.filesystem
384                    .statfs(self, self.request.nodeid().into(), self.reply());
385            }
386            ll::Operation::SetXAttr(x) => {
387                se.filesystem.setxattr(
388                    self,
389                    self.request.nodeid().into(),
390                    x.name(),
391                    x.value(),
392                    x.flags(),
393                    x.position(),
394                    self.reply(),
395                );
396            }
397            ll::Operation::GetXAttr(x) => {
398                se.filesystem.getxattr(
399                    self,
400                    self.request.nodeid().into(),
401                    x.name(),
402                    x.size_u32(),
403                    self.reply(),
404                );
405            }
406            ll::Operation::ListXAttr(x) => {
407                se.filesystem
408                    .listxattr(self, self.request.nodeid().into(), x.size(), self.reply());
409            }
410            ll::Operation::RemoveXAttr(x) => {
411                se.filesystem.removexattr(
412                    self,
413                    self.request.nodeid().into(),
414                    x.name(),
415                    self.reply(),
416                );
417            }
418            ll::Operation::Access(x) => {
419                se.filesystem
420                    .access(self, self.request.nodeid().into(), x.mask(), self.reply());
421            }
422            ll::Operation::Create(x) => {
423                se.filesystem.create(
424                    self,
425                    self.request.nodeid().into(),
426                    x.name().as_ref(),
427                    x.mode(),
428                    x.umask(),
429                    x.flags(),
430                    self.reply(),
431                );
432            }
433            ll::Operation::GetLk(x) => {
434                se.filesystem.getlk(
435                    self,
436                    self.request.nodeid().into(),
437                    x.file_handle().into(),
438                    x.lock_owner().into(),
439                    x.lock().range.0,
440                    x.lock().range.1,
441                    x.lock().typ,
442                    x.lock().pid,
443                    self.reply(),
444                );
445            }
446            ll::Operation::SetLk(x) => {
447                se.filesystem.setlk(
448                    self,
449                    self.request.nodeid().into(),
450                    x.file_handle().into(),
451                    x.lock_owner().into(),
452                    x.lock().range.0,
453                    x.lock().range.1,
454                    x.lock().typ,
455                    x.lock().pid,
456                    false,
457                    self.reply(),
458                );
459            }
460            ll::Operation::SetLkW(x) => {
461                se.filesystem.setlk(
462                    self,
463                    self.request.nodeid().into(),
464                    x.file_handle().into(),
465                    x.lock_owner().into(),
466                    x.lock().range.0,
467                    x.lock().range.1,
468                    x.lock().typ,
469                    x.lock().pid,
470                    true,
471                    self.reply(),
472                );
473            }
474            ll::Operation::BMap(x) => {
475                se.filesystem.bmap(
476                    self,
477                    self.request.nodeid().into(),
478                    x.block_size(),
479                    x.block(),
480                    self.reply(),
481                );
482            }
483
484            ll::Operation::IoCtl(x) => {
485                if x.unrestricted() {
486                    return Err(Errno::ENOSYS);
487                } else {
488                    se.filesystem.ioctl(
489                        self,
490                        self.request.nodeid().into(),
491                        x.file_handle().into(),
492                        x.flags(),
493                        x.command(),
494                        x.in_data(),
495                        x.out_size(),
496                        self.reply(),
497                    );
498                }
499            }
500            ll::Operation::Poll(x) => {
501                let ph = PollHandle::new(se.ch.sender(), x.kernel_handle());
502
503                se.filesystem.poll(
504                    self,
505                    self.request.nodeid().into(),
506                    x.file_handle().into(),
507                    ph,
508                    x.events(),
509                    x.flags(),
510                    self.reply(),
511                );
512            }
513            ll::Operation::NotifyReply(_) => {
514                // TODO: handle FUSE_NOTIFY_REPLY
515                return Err(Errno::ENOSYS);
516            }
517            ll::Operation::BatchForget(x) => {
518                se.filesystem.batch_forget(self, x.nodes()); // no reply
519            }
520            #[cfg(feature = "abi-7-19")]
521            ll::Operation::FAllocate(x) => {
522                se.filesystem.fallocate(
523                    self,
524                    self.request.nodeid().into(),
525                    x.file_handle().into(),
526                    x.offset(),
527                    x.len(),
528                    x.mode(),
529                    self.reply(),
530                );
531            }
532            #[cfg(feature = "abi-7-21")]
533            ll::Operation::ReadDirPlus(x) => {
534                se.filesystem.readdirplus(
535                    self,
536                    self.request.nodeid().into(),
537                    x.file_handle().into(),
538                    x.offset(),
539                    ReplyDirectoryPlus::new(
540                        self.request.unique().into(),
541                        self.ch.clone(),
542                        x.size() as usize,
543                    ),
544                );
545            }
546            #[cfg(feature = "abi-7-23")]
547            ll::Operation::Rename2(x) => {
548                se.filesystem.rename(
549                    self,
550                    x.from().dir.into(),
551                    x.from().name.as_ref(),
552                    x.to().dir.into(),
553                    x.to().name.as_ref(),
554                    x.flags(),
555                    self.reply(),
556                );
557            }
558            #[cfg(feature = "abi-7-24")]
559            ll::Operation::Lseek(x) => {
560                se.filesystem.lseek(
561                    self,
562                    self.request.nodeid().into(),
563                    x.file_handle().into(),
564                    x.offset(),
565                    x.whence(),
566                    self.reply(),
567                );
568            }
569            #[cfg(feature = "abi-7-28")]
570            ll::Operation::CopyFileRange(x) => {
571                let (i, o) = (x.src(), x.dest());
572                se.filesystem.copy_file_range(
573                    self,
574                    i.inode.into(),
575                    i.file_handle.into(),
576                    i.offset,
577                    o.inode.into(),
578                    o.file_handle.into(),
579                    o.offset,
580                    x.len(),
581                    x.flags().try_into().unwrap(),
582                    self.reply(),
583                );
584            }
585            #[cfg(target_os = "macos")]
586            ll::Operation::SetVolName(x) => {
587                se.filesystem.setvolname(self, x.name(), self.reply());
588            }
589            #[cfg(target_os = "macos")]
590            ll::Operation::GetXTimes(x) => {
591                se.filesystem
592                    .getxtimes(self, x.nodeid().into(), self.reply());
593            }
594            #[cfg(target_os = "macos")]
595            ll::Operation::Exchange(x) => {
596                se.filesystem.exchange(
597                    self,
598                    x.from().dir.into(),
599                    x.from().name.as_ref(),
600                    x.to().dir.into(),
601                    x.to().name.as_ref(),
602                    x.options(),
603                    self.reply(),
604                );
605            }
606
607            ll::Operation::CuseInit(_) => {
608                // TODO: handle CUSE_INIT
609                return Err(Errno::ENOSYS);
610            }
611        }
612        Ok(None)
613    }
614
615    /// Create a reply object for this request that can be passed to the filesystem
616    /// implementation and makes sure that a request is replied exactly once
617    fn reply<T: Reply>(&self) -> T {
618        Reply::new(self.request.unique().into(), self.ch.clone())
619    }
620
621    /// Returns the unique identifier of this request
622    #[inline]
623    pub fn unique(&self) -> u64 {
624        self.request.unique().into()
625    }
626
627    /// Returns the uid of this request
628    #[inline]
629    pub fn uid(&self) -> u32 {
630        self.request.uid()
631    }
632
633    /// Returns the gid of this request
634    #[inline]
635    pub fn gid(&self) -> u32 {
636        self.request.gid()
637    }
638
639    /// Returns the pid of this request
640    #[inline]
641    pub fn pid(&self) -> u32 {
642        self.request.pid()
643    }
644
645    /// Returns whether this is a forget request
646    pub fn is_forget(&self) -> bool {
647        matches!(
648            self.request.opcode(),
649            Ok(abi::fuse_opcode::FUSE_FORGET) | Ok(abi::fuse_opcode::FUSE_BATCH_FORGET)
650        )
651    }
652}