fuse/
server.rs

1// Copyright 2019 The ChromiumOS Authors
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use std::cmp::max;
6use std::cmp::min;
7use std::convert::TryInto;
8use std::ffi::CStr;
9use std::io;
10use std::mem::size_of;
11use std::mem::MaybeUninit;
12use std::os::unix::io::AsRawFd;
13use std::time::Duration;
14
15use base::error;
16use base::pagesize;
17use base::Protection;
18use zerocopy::FromBytes;
19use zerocopy::Immutable;
20use zerocopy::IntoBytes;
21
22use crate::filesystem::Context;
23use crate::filesystem::DirEntry;
24use crate::filesystem::DirectoryIterator;
25use crate::filesystem::Entry;
26use crate::filesystem::FileSystem;
27use crate::filesystem::GetxattrReply;
28use crate::filesystem::IoctlReply;
29use crate::filesystem::ListxattrReply;
30use crate::filesystem::ZeroCopyReader;
31use crate::filesystem::ZeroCopyWriter;
32use crate::sys::*;
33use crate::Error;
34use crate::Result;
35
36const DIRENT_PADDING: [u8; 8] = [0; 8];
37
38const SELINUX_XATTR_CSTR: &[u8] = b"security.selinux\0";
39
40/// A trait for reading from the underlying FUSE endpoint.
41pub trait Reader: io::Read {
42    fn read_struct<T: IntoBytes + FromBytes>(&mut self) -> Result<T> {
43        let mut out = T::new_zeroed();
44        self.read_exact(out.as_mut_bytes())
45            .map_err(Error::DecodeMessage)?;
46        Ok(out)
47    }
48}
49
50impl<R: Reader> Reader for &'_ mut R {}
51
52/// A trait for writing to the underlying FUSE endpoint. The FUSE device expects the write
53/// operation to happen in one write transaction. Since there are cases when data needs to be
54/// generated earlier than the header, it implies the writer implementation to keep an internal
55/// buffer. The buffer then can be flushed once header and data are both prepared.
56pub trait Writer: io::Write {
57    /// The type passed in to the closure in `write_at`. For most implementations, this should be
58    /// `Self`.
59    type ClosureWriter: Writer + ZeroCopyWriter;
60
61    /// Allows a closure to generate and write data at the current writer's offset. The current
62    /// writer is passed as a mutable reference to the closure. As an example, this provides an
63    /// adapter for the read implementation of a filesystem to write directly to the final buffer
64    /// without generating the FUSE header first.
65    ///
66    /// Notes: An alternative implementation would be to return a slightly different writer for the
67    /// API client to write to the offset. Since the API needs to be called for more than one time,
68    /// it imposes some complexity to deal with borrowing and mutability. The current approach
69    /// simply does not need to create a different writer, thus no need to deal with the mentioned
70    /// complexity.
71    fn write_at<F>(&mut self, offset: usize, f: F) -> io::Result<usize>
72    where
73        F: Fn(&mut Self::ClosureWriter) -> io::Result<usize>;
74
75    /// Checks if the writer can still accept certain amount of data.
76    fn has_sufficient_buffer(&self, size: u32) -> bool;
77}
78
79impl<W: Writer> Writer for &'_ mut W {
80    type ClosureWriter = W::ClosureWriter;
81
82    fn write_at<F>(&mut self, offset: usize, f: F) -> io::Result<usize>
83    where
84        F: Fn(&mut Self::ClosureWriter) -> io::Result<usize>,
85    {
86        (**self).write_at(offset, f)
87    }
88
89    fn has_sufficient_buffer(&self, size: u32) -> bool {
90        (**self).has_sufficient_buffer(size)
91    }
92}
93
94/// A trait for memory mapping for DAX.
95///
96/// For some transports (like virtio) it may be possible to share a region of memory with the
97/// FUSE kernel driver so that it can access file contents directly without issuing read or
98/// write requests.  In this case the driver will instead send requests to map a section of a
99/// file into the shared memory region.
100pub trait Mapper {
101    /// Maps `size` bytes starting at `file_offset` bytes from within the given `fd` at `mem_offset`
102    /// bytes from the start of the memory region with `prot` protections. `mem_offset` must be
103    /// page aligned.
104    ///
105    /// # Arguments
106    /// * `mem_offset` - Page aligned offset into the memory region in bytes.
107    /// * `size` - Size of memory region in bytes.
108    /// * `fd` - File descriptor to mmap from.
109    /// * `file_offset` - Offset in bytes from the beginning of `fd` to start the mmap.
110    /// * `prot` - Protection of the memory region.
111    fn map(
112        &self,
113        mem_offset: u64,
114        size: usize,
115        fd: &dyn AsRawFd,
116        file_offset: u64,
117        prot: Protection,
118    ) -> io::Result<()>;
119
120    /// Unmaps `size` bytes at `offset` bytes from the start of the memory region. `offset` must be
121    /// page aligned.
122    ///
123    /// # Arguments
124    /// * `offset` - Page aligned offset into the arena in bytes.
125    /// * `size` - Size of memory region in bytes.
126    fn unmap(&self, offset: u64, size: u64) -> io::Result<()>;
127}
128
129impl<M: Mapper> Mapper for &M {
130    fn map(
131        &self,
132        mem_offset: u64,
133        size: usize,
134        fd: &dyn AsRawFd,
135        file_offset: u64,
136        prot: Protection,
137    ) -> io::Result<()> {
138        (**self).map(mem_offset, size, fd, file_offset, prot)
139    }
140
141    fn unmap(&self, offset: u64, size: u64) -> io::Result<()> {
142        (**self).unmap(offset, size)
143    }
144}
145
146pub struct Server<F: FileSystem + Sync> {
147    fs: F,
148}
149
150impl<F: FileSystem + Sync> Server<F> {
151    pub fn new(fs: F) -> Server<F> {
152        Server { fs }
153    }
154
155    pub fn handle_message<R: Reader + ZeroCopyReader, W: Writer + ZeroCopyWriter, M: Mapper>(
156        &self,
157        mut r: R,
158        w: W,
159        mapper: M,
160    ) -> Result<usize> {
161        let in_header: InHeader = r.read_struct()?;
162        cros_tracing::trace_simple_print!("fuse server: handle_message: in_header={:?}", in_header);
163
164        if in_header.len
165            > size_of::<InHeader>() as u32 + size_of::<WriteIn>() as u32 + self.fs.max_buffer_size()
166        {
167            return reply_error(
168                io::Error::from_raw_os_error(libc::ENOMEM),
169                in_header.unique,
170                w,
171            );
172        }
173        match Opcode::n(in_header.opcode) {
174            Some(Opcode::Lookup) => self.lookup(in_header, r, w),
175            Some(Opcode::Forget) => self.forget(in_header, r), // No reply.
176            Some(Opcode::Getattr) => self.getattr(in_header, r, w),
177            Some(Opcode::Setattr) => self.setattr(in_header, r, w),
178            Some(Opcode::Readlink) => self.readlink(in_header, w),
179            Some(Opcode::Symlink) => self.symlink(in_header, r, w),
180            Some(Opcode::Mknod) => self.mknod(in_header, r, w),
181            Some(Opcode::Mkdir) => self.mkdir(in_header, r, w),
182            Some(Opcode::Unlink) => self.unlink(in_header, r, w),
183            Some(Opcode::Rmdir) => self.rmdir(in_header, r, w),
184            Some(Opcode::Rename) => self.rename(in_header, r, w),
185            Some(Opcode::Link) => self.link(in_header, r, w),
186            Some(Opcode::Open) => self.open(in_header, r, w),
187            Some(Opcode::Read) => self.read(in_header, r, w),
188            Some(Opcode::Write) => self.write(in_header, r, w),
189            Some(Opcode::Statfs) => self.statfs(in_header, w),
190            Some(Opcode::Release) => self.release(in_header, r, w),
191            Some(Opcode::Fsync) => self.fsync(in_header, r, w),
192            Some(Opcode::Setxattr) => self.setxattr(in_header, r, w),
193            Some(Opcode::Getxattr) => self.getxattr(in_header, r, w),
194            Some(Opcode::Listxattr) => self.listxattr(in_header, r, w),
195            Some(Opcode::Removexattr) => self.removexattr(in_header, r, w),
196            Some(Opcode::Flush) => self.flush(in_header, r, w),
197            Some(Opcode::Init) => self.init(in_header, r, w),
198            Some(Opcode::Opendir) => self.opendir(in_header, r, w),
199            Some(Opcode::Readdir) => self.readdir(in_header, r, w),
200            Some(Opcode::Releasedir) => self.releasedir(in_header, r, w),
201            Some(Opcode::Fsyncdir) => self.fsyncdir(in_header, r, w),
202            Some(Opcode::Getlk) => self.getlk(in_header, r, w),
203            Some(Opcode::Setlk) => self.setlk(in_header, r, w),
204            Some(Opcode::Setlkw) => self.setlkw(in_header, r, w),
205            Some(Opcode::Access) => self.access(in_header, r, w),
206            Some(Opcode::Create) => self.create(in_header, r, w),
207            Some(Opcode::Interrupt) => self.interrupt(in_header),
208            Some(Opcode::Bmap) => self.bmap(in_header, r, w),
209            Some(Opcode::Destroy) => self.destroy(),
210            Some(Opcode::Ioctl) => self.ioctl(in_header, r, w),
211            Some(Opcode::Poll) => self.poll(in_header, r, w),
212            Some(Opcode::NotifyReply) => self.notify_reply(in_header, r, w),
213            Some(Opcode::BatchForget) => self.batch_forget(in_header, r, w),
214            Some(Opcode::Fallocate) => self.fallocate(in_header, r, w),
215            Some(Opcode::Readdirplus) => self.readdirplus(in_header, r, w),
216            Some(Opcode::Rename2) => self.rename2(in_header, r, w),
217            Some(Opcode::Lseek) => self.lseek(in_header, r, w),
218            Some(Opcode::CopyFileRange) => self.copy_file_range(in_header, r, w),
219            Some(Opcode::ChromeOsTmpfile) => self.chromeos_tmpfile(in_header, r, w),
220            Some(Opcode::SetUpMapping) => self.set_up_mapping(in_header, r, w, mapper),
221            Some(Opcode::RemoveMapping) => self.remove_mapping(in_header, r, w, mapper),
222            Some(Opcode::OpenAtomic) => self.open_atomic(in_header, r, w),
223            None => reply_error(
224                io::Error::from_raw_os_error(libc::ENOSYS),
225                in_header.unique,
226                w,
227            ),
228        }
229    }
230
231    fn lookup<R: Reader, W: Writer>(&self, in_header: InHeader, mut r: R, w: W) -> Result<usize> {
232        let namelen = (in_header.len as usize)
233            .checked_sub(size_of::<InHeader>())
234            .ok_or(Error::InvalidHeaderLength)?;
235
236        let mut buf = vec![0; namelen];
237
238        r.read_exact(&mut buf).map_err(Error::DecodeMessage)?;
239
240        let name = bytes_to_cstr(&buf)?;
241
242        match self
243            .fs
244            .lookup(Context::from(in_header), in_header.nodeid.into(), name)
245        {
246            Ok(entry) => {
247                let out = EntryOut::from(entry);
248
249                reply_ok(Some(out), None, in_header.unique, w)
250            }
251            Err(e) => reply_error(e, in_header.unique, w),
252        }
253    }
254
255    fn forget<R: Reader>(&self, in_header: InHeader, mut r: R) -> Result<usize> {
256        let ForgetIn { nlookup } = r.read_struct()?;
257
258        self.fs
259            .forget(Context::from(in_header), in_header.nodeid.into(), nlookup);
260
261        // There is no reply for forget messages.
262        Ok(0)
263    }
264
265    fn getattr<R: Reader, W: Writer>(&self, in_header: InHeader, mut r: R, w: W) -> Result<usize> {
266        let GetattrIn {
267            flags,
268            dummy: _,
269            fh,
270        } = r.read_struct()?;
271
272        let handle = if (flags & GETATTR_FH) != 0 {
273            Some(fh.into())
274        } else {
275            None
276        };
277
278        match self
279            .fs
280            .getattr(Context::from(in_header), in_header.nodeid.into(), handle)
281        {
282            Ok((st, timeout)) => {
283                let out = AttrOut {
284                    attr_valid: timeout.as_secs(),
285                    attr_valid_nsec: timeout.subsec_nanos(),
286                    dummy: 0,
287                    attr: st.into(),
288                };
289                reply_ok(Some(out), None, in_header.unique, w)
290            }
291            Err(e) => reply_error(e, in_header.unique, w),
292        }
293    }
294
295    fn setattr<R: Reader, W: Writer>(&self, in_header: InHeader, mut r: R, w: W) -> Result<usize> {
296        let setattr_in: SetattrIn = r.read_struct()?;
297
298        let handle = if setattr_in.valid & FATTR_FH != 0 {
299            Some(setattr_in.fh.into())
300        } else {
301            None
302        };
303
304        let valid = SetattrValid::from_bits_truncate(setattr_in.valid);
305
306        let st: libc::stat64 = setattr_in.into();
307
308        match self.fs.setattr(
309            Context::from(in_header),
310            in_header.nodeid.into(),
311            st,
312            handle,
313            valid,
314        ) {
315            Ok((st, timeout)) => {
316                let out = AttrOut {
317                    attr_valid: timeout.as_secs(),
318                    attr_valid_nsec: timeout.subsec_nanos(),
319                    dummy: 0,
320                    attr: st.into(),
321                };
322                reply_ok(Some(out), None, in_header.unique, w)
323            }
324            Err(e) => reply_error(e, in_header.unique, w),
325        }
326    }
327
328    fn readlink<W: Writer>(&self, in_header: InHeader, w: W) -> Result<usize> {
329        match self
330            .fs
331            .readlink(Context::from(in_header), in_header.nodeid.into())
332        {
333            Ok(linkname) => {
334                // We need to disambiguate the option type here even though it is `None`.
335                reply_ok(None::<u8>, Some(&linkname), in_header.unique, w)
336            }
337            Err(e) => reply_error(e, in_header.unique, w),
338        }
339    }
340
341    fn symlink<R: Reader, W: Writer>(&self, in_header: InHeader, mut r: R, w: W) -> Result<usize> {
342        // Unfortunately the name and linkname are encoded one after another and
343        // separated by a nul character.
344        let len = (in_header.len as usize)
345            .checked_sub(size_of::<InHeader>())
346            .ok_or(Error::InvalidHeaderLength)?;
347        let mut buf = vec![0; len];
348
349        r.read_exact(&mut buf).map_err(Error::DecodeMessage)?;
350
351        let mut iter = buf.split_inclusive(|&c| c == b'\0');
352        let name = iter
353            .next()
354            .ok_or(Error::MissingParameter)
355            .and_then(bytes_to_cstr)?;
356        let linkname = iter
357            .next()
358            .ok_or(Error::MissingParameter)
359            .and_then(bytes_to_cstr)?;
360
361        let split_pos = name.to_bytes_with_nul().len() + linkname.to_bytes_with_nul().len();
362        let security_ctx = parse_selinux_xattr(&buf[split_pos..])?;
363
364        match self.fs.symlink(
365            Context::from(in_header),
366            linkname,
367            in_header.nodeid.into(),
368            name,
369            security_ctx,
370        ) {
371            Ok(entry) => {
372                let out = EntryOut::from(entry);
373
374                reply_ok(Some(out), None, in_header.unique, w)
375            }
376            Err(e) => reply_error(e, in_header.unique, w),
377        }
378    }
379
380    fn mknod<R: Reader, W: Writer>(&self, in_header: InHeader, mut r: R, w: W) -> Result<usize> {
381        let MknodIn {
382            mode, rdev, umask, ..
383        } = r.read_struct()?;
384
385        let buflen = (in_header.len as usize)
386            .checked_sub(size_of::<InHeader>())
387            .and_then(|l| l.checked_sub(size_of::<MknodIn>()))
388            .ok_or(Error::InvalidHeaderLength)?;
389        let mut buf = vec![0; buflen];
390
391        r.read_exact(&mut buf).map_err(Error::DecodeMessage)?;
392
393        let mut iter = buf.split_inclusive(|&c| c == b'\0');
394        let name = iter
395            .next()
396            .ok_or(Error::MissingParameter)
397            .and_then(bytes_to_cstr)?;
398
399        let split_pos = name.to_bytes_with_nul().len();
400        let security_ctx = parse_selinux_xattr(&buf[split_pos..])?;
401
402        match self.fs.mknod(
403            Context::from(in_header),
404            in_header.nodeid.into(),
405            name,
406            mode,
407            rdev,
408            umask,
409            security_ctx,
410        ) {
411            Ok(entry) => {
412                let out = EntryOut::from(entry);
413
414                reply_ok(Some(out), None, in_header.unique, w)
415            }
416            Err(e) => reply_error(e, in_header.unique, w),
417        }
418    }
419
420    fn mkdir<R: Reader, W: Writer>(&self, in_header: InHeader, mut r: R, w: W) -> Result<usize> {
421        let MkdirIn { mode, umask } = r.read_struct()?;
422
423        let buflen = (in_header.len as usize)
424            .checked_sub(size_of::<InHeader>())
425            .and_then(|l| l.checked_sub(size_of::<MkdirIn>()))
426            .ok_or(Error::InvalidHeaderLength)?;
427        let mut buf = vec![0; buflen];
428
429        r.read_exact(&mut buf).map_err(Error::DecodeMessage)?;
430
431        let mut iter = buf.split_inclusive(|&c| c == b'\0');
432        let name = iter
433            .next()
434            .ok_or(Error::MissingParameter)
435            .and_then(bytes_to_cstr)?;
436
437        let split_pos = name.to_bytes_with_nul().len();
438        let security_ctx = parse_selinux_xattr(&buf[split_pos..])?;
439
440        match self.fs.mkdir(
441            Context::from(in_header),
442            in_header.nodeid.into(),
443            name,
444            mode,
445            umask,
446            security_ctx,
447        ) {
448            Ok(entry) => {
449                let out = EntryOut::from(entry);
450
451                reply_ok(Some(out), None, in_header.unique, w)
452            }
453            Err(e) => reply_error(e, in_header.unique, w),
454        }
455    }
456
457    fn chromeos_tmpfile<R: Reader, W: Writer>(
458        &self,
459        in_header: InHeader,
460        mut r: R,
461        w: W,
462    ) -> Result<usize> {
463        let ChromeOsTmpfileIn { mode, umask } = r.read_struct()?;
464
465        let len = (in_header.len as usize)
466            .checked_sub(size_of::<InHeader>())
467            .and_then(|l| l.checked_sub(size_of::<ChromeOsTmpfileIn>()))
468            .ok_or(Error::InvalidHeaderLength)?;
469        let mut buf = vec![0; len];
470
471        r.read_exact(&mut buf).map_err(Error::DecodeMessage)?;
472
473        let security_ctx = parse_selinux_xattr(&buf)?;
474
475        match self.fs.chromeos_tmpfile(
476            Context::from(in_header),
477            in_header.nodeid.into(),
478            mode,
479            umask,
480            security_ctx,
481        ) {
482            Ok(entry) => {
483                let out = EntryOut::from(entry);
484
485                reply_ok(Some(out), None, in_header.unique, w)
486            }
487            Err(e) => reply_error(e, in_header.unique, w),
488        }
489    }
490
491    fn unlink<R: Reader, W: Writer>(&self, in_header: InHeader, mut r: R, w: W) -> Result<usize> {
492        let namelen = (in_header.len as usize)
493            .checked_sub(size_of::<InHeader>())
494            .ok_or(Error::InvalidHeaderLength)?;
495        let mut name = vec![0; namelen];
496
497        r.read_exact(&mut name).map_err(Error::DecodeMessage)?;
498
499        match self.fs.unlink(
500            Context::from(in_header),
501            in_header.nodeid.into(),
502            bytes_to_cstr(&name)?,
503        ) {
504            Ok(()) => reply_ok(None::<u8>, None, in_header.unique, w),
505            Err(e) => reply_error(e, in_header.unique, w),
506        }
507    }
508
509    fn rmdir<R: Reader, W: Writer>(&self, in_header: InHeader, mut r: R, w: W) -> Result<usize> {
510        let namelen = (in_header.len as usize)
511            .checked_sub(size_of::<InHeader>())
512            .ok_or(Error::InvalidHeaderLength)?;
513        let mut name = vec![0; namelen];
514
515        r.read_exact(&mut name).map_err(Error::DecodeMessage)?;
516
517        match self.fs.rmdir(
518            Context::from(in_header),
519            in_header.nodeid.into(),
520            bytes_to_cstr(&name)?,
521        ) {
522            Ok(()) => reply_ok(None::<u8>, None, in_header.unique, w),
523            Err(e) => reply_error(e, in_header.unique, w),
524        }
525    }
526
527    fn do_rename<R: Reader, W: Writer>(
528        &self,
529        in_header: InHeader,
530        msg_size: usize,
531        newdir: u64,
532        flags: u32,
533        mut r: R,
534        w: W,
535    ) -> Result<usize> {
536        let buflen = (in_header.len as usize)
537            .checked_sub(size_of::<InHeader>())
538            .and_then(|l| l.checked_sub(msg_size))
539            .ok_or(Error::InvalidHeaderLength)?;
540        let mut buf = vec![0; buflen];
541
542        r.read_exact(&mut buf).map_err(Error::DecodeMessage)?;
543
544        // We want to include the '\0' byte in the first slice.
545        let split_pos = buf
546            .iter()
547            .position(|c| *c == b'\0')
548            .map(|p| p + 1)
549            .ok_or(Error::MissingParameter)?;
550
551        let (oldname, newname) = buf.split_at(split_pos);
552
553        match self.fs.rename(
554            Context::from(in_header),
555            in_header.nodeid.into(),
556            bytes_to_cstr(oldname)?,
557            newdir.into(),
558            bytes_to_cstr(newname)?,
559            flags,
560        ) {
561            Ok(()) => reply_ok(None::<u8>, None, in_header.unique, w),
562            Err(e) => reply_error(e, in_header.unique, w),
563        }
564    }
565
566    fn rename<R: Reader, W: Writer>(&self, in_header: InHeader, mut r: R, w: W) -> Result<usize> {
567        let RenameIn { newdir } = r.read_struct()?;
568
569        self.do_rename(in_header, size_of::<RenameIn>(), newdir, 0, r, w)
570    }
571
572    fn rename2<R: Reader, W: Writer>(&self, in_header: InHeader, mut r: R, w: W) -> Result<usize> {
573        let Rename2In { newdir, flags, .. } = r.read_struct()?;
574
575        #[allow(clippy::unnecessary_cast)]
576        let flags = flags & (libc::RENAME_EXCHANGE | libc::RENAME_NOREPLACE) as u32;
577
578        self.do_rename(in_header, size_of::<Rename2In>(), newdir, flags, r, w)
579    }
580
581    fn link<R: Reader, W: Writer>(&self, in_header: InHeader, mut r: R, w: W) -> Result<usize> {
582        let LinkIn { oldnodeid } = r.read_struct()?;
583
584        let namelen = (in_header.len as usize)
585            .checked_sub(size_of::<InHeader>())
586            .and_then(|l| l.checked_sub(size_of::<LinkIn>()))
587            .ok_or(Error::InvalidHeaderLength)?;
588        let mut name = vec![0; namelen];
589
590        r.read_exact(&mut name).map_err(Error::DecodeMessage)?;
591
592        match self.fs.link(
593            Context::from(in_header),
594            oldnodeid.into(),
595            in_header.nodeid.into(),
596            bytes_to_cstr(&name)?,
597        ) {
598            Ok(entry) => {
599                let out = EntryOut::from(entry);
600
601                reply_ok(Some(out), None, in_header.unique, w)
602            }
603            Err(e) => reply_error(e, in_header.unique, w),
604        }
605    }
606
607    fn open<R: Reader, W: Writer>(&self, in_header: InHeader, mut r: R, w: W) -> Result<usize> {
608        let OpenIn { flags, .. } = r.read_struct()?;
609
610        match self
611            .fs
612            .open(Context::from(in_header), in_header.nodeid.into(), flags)
613        {
614            Ok((handle, opts)) => {
615                let out = OpenOut {
616                    fh: handle.map(Into::into).unwrap_or(0),
617                    open_flags: opts.bits(),
618                    ..Default::default()
619                };
620
621                reply_ok(Some(out), None, in_header.unique, w)
622            }
623            Err(e) => reply_error(e, in_header.unique, w),
624        }
625    }
626
627    fn read<R: Reader, W: ZeroCopyWriter + Writer>(
628        &self,
629        in_header: InHeader,
630        mut r: R,
631        mut w: W,
632    ) -> Result<usize> {
633        let ReadIn {
634            fh,
635            offset,
636            size,
637            read_flags,
638            lock_owner,
639            flags,
640            ..
641        } = r.read_struct()?;
642
643        if size > self.fs.max_buffer_size() {
644            return reply_error(
645                io::Error::from_raw_os_error(libc::ENOMEM),
646                in_header.unique,
647                w,
648            );
649        }
650
651        let owner = if read_flags & READ_LOCKOWNER != 0 {
652            Some(lock_owner)
653        } else {
654            None
655        };
656
657        // Skip for the header size to write the data first.
658        match w.write_at(size_of::<OutHeader>(), |writer| {
659            self.fs.read(
660                Context::from(in_header),
661                in_header.nodeid.into(),
662                fh.into(),
663                writer,
664                size,
665                offset,
666                owner,
667                flags,
668            )
669        }) {
670            Ok(count) => {
671                // Don't use `reply_ok` because we need to set a custom size length for the
672                // header.
673                let out = OutHeader {
674                    len: (size_of::<OutHeader>() + count) as u32,
675                    error: 0,
676                    unique: in_header.unique,
677                };
678
679                w.write_all(out.as_bytes()).map_err(Error::EncodeMessage)?;
680                w.flush().map_err(Error::FlushMessage)?;
681                Ok(out.len as usize)
682            }
683            Err(e) => reply_error(e, in_header.unique, w),
684        }
685    }
686
687    fn write<R: Reader + ZeroCopyReader, W: Writer>(
688        &self,
689        in_header: InHeader,
690        mut r: R,
691        w: W,
692    ) -> Result<usize> {
693        let WriteIn {
694            fh,
695            offset,
696            size,
697            write_flags,
698            lock_owner,
699            flags,
700            ..
701        } = r.read_struct()?;
702
703        if size > self.fs.max_buffer_size() {
704            return reply_error(
705                io::Error::from_raw_os_error(libc::ENOMEM),
706                in_header.unique,
707                w,
708            );
709        }
710
711        let owner = if write_flags & WRITE_LOCKOWNER != 0 {
712            Some(lock_owner)
713        } else {
714            None
715        };
716
717        let delayed_write = write_flags & WRITE_CACHE != 0;
718
719        match self.fs.write(
720            Context::from(in_header),
721            in_header.nodeid.into(),
722            fh.into(),
723            r,
724            size,
725            offset,
726            owner,
727            delayed_write,
728            flags,
729        ) {
730            Ok(count) => {
731                let out = WriteOut {
732                    size: count as u32,
733                    ..Default::default()
734                };
735
736                reply_ok(Some(out), None, in_header.unique, w)
737            }
738            Err(e) => reply_error(e, in_header.unique, w),
739        }
740    }
741
742    fn statfs<W: Writer>(&self, in_header: InHeader, w: W) -> Result<usize> {
743        match self
744            .fs
745            .statfs(Context::from(in_header), in_header.nodeid.into())
746        {
747            Ok(st) => reply_ok(Some(Kstatfs::from(st)), None, in_header.unique, w),
748            Err(e) => reply_error(e, in_header.unique, w),
749        }
750    }
751
752    fn release<R: Reader, W: Writer>(&self, in_header: InHeader, mut r: R, w: W) -> Result<usize> {
753        let ReleaseIn {
754            fh,
755            flags,
756            release_flags,
757            lock_owner,
758        } = r.read_struct()?;
759
760        let flush = release_flags & RELEASE_FLUSH != 0;
761        let flock_release = release_flags & RELEASE_FLOCK_UNLOCK != 0;
762        let lock_owner = if flush || flock_release {
763            Some(lock_owner)
764        } else {
765            None
766        };
767
768        match self.fs.release(
769            Context::from(in_header),
770            in_header.nodeid.into(),
771            flags,
772            fh.into(),
773            flush,
774            flock_release,
775            lock_owner,
776        ) {
777            Ok(()) => reply_ok(None::<u8>, None, in_header.unique, w),
778            Err(e) => reply_error(e, in_header.unique, w),
779        }
780    }
781
782    fn fsync<R: Reader, W: Writer>(&self, in_header: InHeader, mut r: R, w: W) -> Result<usize> {
783        let FsyncIn {
784            fh, fsync_flags, ..
785        } = r.read_struct()?;
786        let datasync = fsync_flags & 0x1 != 0;
787
788        match self.fs.fsync(
789            Context::from(in_header),
790            in_header.nodeid.into(),
791            datasync,
792            fh.into(),
793        ) {
794            Ok(()) => reply_ok(None::<u8>, None, in_header.unique, w),
795            Err(e) => reply_error(e, in_header.unique, w),
796        }
797    }
798
799    fn setxattr<R: Reader, W: Writer>(&self, in_header: InHeader, mut r: R, w: W) -> Result<usize> {
800        let SetxattrIn { size, flags } = r.read_struct()?;
801
802        // The name and value and encoded one after another and separated by a '\0' character.
803        let len = (in_header.len as usize)
804            .checked_sub(size_of::<InHeader>())
805            .and_then(|l| l.checked_sub(size_of::<SetxattrIn>()))
806            .ok_or(Error::InvalidHeaderLength)?;
807        let mut buf = vec![0; len];
808
809        r.read_exact(&mut buf).map_err(Error::DecodeMessage)?;
810
811        // We want to include the '\0' byte in the first slice.
812        let split_pos = buf
813            .iter()
814            .position(|c| *c == b'\0')
815            .map(|p| p + 1)
816            .ok_or(Error::MissingParameter)?;
817
818        let (name, value) = buf.split_at(split_pos);
819
820        if size != value.len() as u32 {
821            return Err(Error::InvalidXattrSize(size, value.len()));
822        }
823
824        match self.fs.setxattr(
825            Context::from(in_header),
826            in_header.nodeid.into(),
827            bytes_to_cstr(name)?,
828            value,
829            flags,
830        ) {
831            Ok(()) => reply_ok(None::<u8>, None, in_header.unique, w),
832            Err(e) => reply_error(e, in_header.unique, w),
833        }
834    }
835
836    fn getxattr<R: Reader, W: Writer>(&self, in_header: InHeader, mut r: R, w: W) -> Result<usize> {
837        let GetxattrIn { size, .. } = r.read_struct()?;
838
839        let namelen = (in_header.len as usize)
840            .checked_sub(size_of::<InHeader>())
841            .and_then(|l| l.checked_sub(size_of::<GetxattrIn>()))
842            .ok_or(Error::InvalidHeaderLength)?;
843        let mut name = vec![0; namelen];
844
845        r.read_exact(&mut name).map_err(Error::DecodeMessage)?;
846
847        if size > self.fs.max_buffer_size() {
848            return reply_error(
849                io::Error::from_raw_os_error(libc::ENOMEM),
850                in_header.unique,
851                w,
852            );
853        }
854
855        match self.fs.getxattr(
856            Context::from(in_header),
857            in_header.nodeid.into(),
858            bytes_to_cstr(&name)?,
859            size,
860        ) {
861            Ok(GetxattrReply::Value(val)) => reply_ok(None::<u8>, Some(&val), in_header.unique, w),
862            Ok(GetxattrReply::Count(count)) => {
863                let out = GetxattrOut {
864                    size: count,
865                    ..Default::default()
866                };
867
868                reply_ok(Some(out), None, in_header.unique, w)
869            }
870            Err(e) => reply_error(e, in_header.unique, w),
871        }
872    }
873
874    fn listxattr<R: Reader, W: Writer>(
875        &self,
876        in_header: InHeader,
877        mut r: R,
878        w: W,
879    ) -> Result<usize> {
880        let GetxattrIn { size, .. } = r.read_struct()?;
881
882        if size > self.fs.max_buffer_size() {
883            return reply_error(
884                io::Error::from_raw_os_error(libc::ENOMEM),
885                in_header.unique,
886                w,
887            );
888        }
889
890        match self
891            .fs
892            .listxattr(Context::from(in_header), in_header.nodeid.into(), size)
893        {
894            Ok(ListxattrReply::Names(val)) => reply_ok(None::<u8>, Some(&val), in_header.unique, w),
895            Ok(ListxattrReply::Count(count)) => {
896                let out = GetxattrOut {
897                    size: count,
898                    ..Default::default()
899                };
900
901                reply_ok(Some(out), None, in_header.unique, w)
902            }
903            Err(e) => reply_error(e, in_header.unique, w),
904        }
905    }
906
907    fn removexattr<R: Reader, W: Writer>(
908        &self,
909        in_header: InHeader,
910        mut r: R,
911        w: W,
912    ) -> Result<usize> {
913        let namelen = (in_header.len as usize)
914            .checked_sub(size_of::<InHeader>())
915            .ok_or(Error::InvalidHeaderLength)?;
916
917        let mut buf = vec![0; namelen];
918
919        r.read_exact(&mut buf).map_err(Error::DecodeMessage)?;
920
921        let name = bytes_to_cstr(&buf)?;
922
923        match self
924            .fs
925            .removexattr(Context::from(in_header), in_header.nodeid.into(), name)
926        {
927            Ok(()) => reply_ok(None::<u8>, None, in_header.unique, w),
928            Err(e) => reply_error(e, in_header.unique, w),
929        }
930    }
931
932    fn flush<R: Reader, W: Writer>(&self, in_header: InHeader, mut r: R, w: W) -> Result<usize> {
933        let FlushIn {
934            fh,
935            unused: _,
936            padding: _,
937            lock_owner,
938        } = r.read_struct()?;
939
940        match self.fs.flush(
941            Context::from(in_header),
942            in_header.nodeid.into(),
943            fh.into(),
944            lock_owner,
945        ) {
946            Ok(()) => reply_ok(None::<u8>, None, in_header.unique, w),
947            Err(e) => reply_error(e, in_header.unique, w),
948        }
949    }
950
951    fn init<R: Reader, W: Writer>(&self, in_header: InHeader, mut r: R, w: W) -> Result<usize> {
952        cros_tracing::trace_simple_print!("fuse server: init: in_header={:?}", in_header);
953        let InitIn {
954            major,
955            minor,
956            max_readahead,
957            flags,
958        } = r.read_struct()?;
959
960        if major < KERNEL_VERSION {
961            error!("Unsupported fuse protocol version: {}.{}", major, minor);
962            return reply_error(
963                io::Error::from_raw_os_error(libc::EPROTO),
964                in_header.unique,
965                w,
966            );
967        }
968
969        if major > KERNEL_VERSION {
970            // Wait for the kernel to reply back with a 7.X version.
971            let out = InitOut {
972                major: KERNEL_VERSION,
973                minor: KERNEL_MINOR_VERSION,
974                ..Default::default()
975            };
976
977            return reply_ok(Some(out), None, in_header.unique, w);
978        }
979
980        if minor < OLDEST_SUPPORTED_KERNEL_MINOR_VERSION {
981            error!(
982                "Unsupported fuse protocol minor version: {}.{}",
983                major, minor
984            );
985            return reply_error(
986                io::Error::from_raw_os_error(libc::EPROTO),
987                in_header.unique,
988                w,
989            );
990        }
991
992        let InitInExt { flags2, .. } =
993            if (FsOptions::from_bits_truncate(u64::from(flags)) & FsOptions::INIT_EXT).is_empty() {
994                InitInExt::default()
995            } else {
996                r.read_struct()?
997            };
998
999        // These fuse features are supported by this server by default.
1000        let supported = FsOptions::ASYNC_READ
1001            | FsOptions::PARALLEL_DIROPS
1002            | FsOptions::BIG_WRITES
1003            | FsOptions::AUTO_INVAL_DATA
1004            | FsOptions::HANDLE_KILLPRIV
1005            | FsOptions::ASYNC_DIO
1006            | FsOptions::HAS_IOCTL_DIR
1007            | FsOptions::DO_READDIRPLUS
1008            | FsOptions::READDIRPLUS_AUTO
1009            | FsOptions::ATOMIC_O_TRUNC
1010            | FsOptions::MAX_PAGES
1011            | FsOptions::MAP_ALIGNMENT
1012            | FsOptions::INIT_EXT;
1013
1014        let capable = FsOptions::from_bits_truncate(u64::from(flags) | u64::from(flags2) << 32);
1015
1016        match self.fs.init(capable) {
1017            Ok(want) => {
1018                let mut enabled = capable & (want | supported);
1019
1020                // HANDLE_KILLPRIV doesn't work correctly when writeback caching is enabled so turn
1021                // it off.
1022                if enabled.contains(FsOptions::WRITEBACK_CACHE) {
1023                    enabled.remove(FsOptions::HANDLE_KILLPRIV);
1024                }
1025
1026                // ATOMIC_O_TRUNC doesn't work with ZERO_MESSAGE_OPEN.
1027                if enabled.contains(FsOptions::ZERO_MESSAGE_OPEN) {
1028                    enabled.remove(FsOptions::ATOMIC_O_TRUNC);
1029                }
1030
1031                let max_write = self.fs.max_buffer_size();
1032                let max_pages = min(
1033                    max(max_readahead, max_write) / pagesize() as u32,
1034                    u16::MAX as u32,
1035                ) as u16;
1036                let out = InitOut {
1037                    major: KERNEL_VERSION,
1038                    minor: KERNEL_MINOR_VERSION,
1039                    max_readahead,
1040                    flags: enabled.bits() as u32,
1041                    max_background: u16::MAX,
1042                    congestion_threshold: (u16::MAX / 4) * 3,
1043                    max_write,
1044                    time_gran: 1, // nanoseconds
1045                    max_pages,
1046                    map_alignment: pagesize().trailing_zeros() as u16,
1047                    flags2: (enabled.bits() >> 32) as u32,
1048                    ..Default::default()
1049                };
1050
1051                reply_ok(Some(out), None, in_header.unique, w)
1052            }
1053            Err(e) => reply_error(e, in_header.unique, w),
1054        }
1055    }
1056
1057    fn opendir<R: Reader, W: Writer>(&self, in_header: InHeader, mut r: R, w: W) -> Result<usize> {
1058        let OpenIn { flags, .. } = r.read_struct()?;
1059
1060        match self
1061            .fs
1062            .opendir(Context::from(in_header), in_header.nodeid.into(), flags)
1063        {
1064            Ok((handle, opts)) => {
1065                let out = OpenOut {
1066                    fh: handle.map(Into::into).unwrap_or(0),
1067                    open_flags: opts.bits(),
1068                    ..Default::default()
1069                };
1070
1071                reply_ok(Some(out), None, in_header.unique, w)
1072            }
1073            Err(e) => reply_error(e, in_header.unique, w),
1074        }
1075    }
1076
1077    fn readdir<R: Reader, W: Writer>(
1078        &self,
1079        in_header: InHeader,
1080        mut r: R,
1081        mut w: W,
1082    ) -> Result<usize> {
1083        let ReadIn {
1084            fh, offset, size, ..
1085        } = r.read_struct()?;
1086
1087        if size > self.fs.max_buffer_size() {
1088            return reply_error(
1089                io::Error::from_raw_os_error(libc::ENOMEM),
1090                in_header.unique,
1091                w,
1092            );
1093        }
1094
1095        if !w.has_sufficient_buffer(size) {
1096            return reply_error(
1097                io::Error::from_raw_os_error(libc::ENOMEM),
1098                in_header.unique,
1099                w,
1100            );
1101        }
1102
1103        // Skip over enough bytes for the header.
1104        let unique = in_header.unique;
1105        let result = w.write_at(size_of::<OutHeader>(), |cursor| {
1106            match self.fs.readdir(
1107                Context::from(in_header),
1108                in_header.nodeid.into(),
1109                fh.into(),
1110                size,
1111                offset,
1112            ) {
1113                Ok(mut entries) => {
1114                    let mut total_written = 0;
1115                    while let Some(dirent) = entries.next() {
1116                        let remaining = (size as usize).saturating_sub(total_written);
1117                        match add_dirent(cursor, remaining, &dirent, None) {
1118                            // No more space left in the buffer.
1119                            Ok(0) => break,
1120                            Ok(bytes_written) => {
1121                                total_written += bytes_written;
1122                            }
1123                            Err(e) => return Err(e),
1124                        }
1125                    }
1126                    Ok(total_written)
1127                }
1128                Err(e) => Err(e),
1129            }
1130        });
1131
1132        match result {
1133            Ok(total_written) => reply_readdir(total_written, unique, w),
1134            Err(e) => reply_error(e, unique, w),
1135        }
1136    }
1137
1138    fn lookup_dirent_attribute(
1139        &self,
1140        in_header: &InHeader,
1141        dir_entry: &DirEntry,
1142    ) -> io::Result<Entry> {
1143        let parent = in_header.nodeid.into();
1144        let name = dir_entry.name.to_bytes();
1145        let entry = if name == b"." || name == b".." {
1146            // Don't do lookups on the current directory or the parent directory.
1147            // SAFETY: struct only contains integer fields and any value is valid.
1148            let mut attr = unsafe { MaybeUninit::<libc::stat64>::zeroed().assume_init() };
1149            attr.st_ino = dir_entry.ino;
1150            attr.st_mode = dir_entry.type_;
1151
1152            // We use 0 for the inode value to indicate a negative entry.
1153            Entry {
1154                inode: 0,
1155                generation: 0,
1156                attr,
1157                attr_timeout: Duration::from_secs(0),
1158                entry_timeout: Duration::from_secs(0),
1159            }
1160        } else {
1161            self.fs
1162                .lookup(Context::from(*in_header), parent, dir_entry.name)?
1163        };
1164
1165        Ok(entry)
1166    }
1167
1168    fn readdirplus<R: Reader, W: Writer>(
1169        &self,
1170        in_header: InHeader,
1171        mut r: R,
1172        mut w: W,
1173    ) -> Result<usize> {
1174        cros_tracing::trace_simple_print!("fuse server: readdirplus: in_header={:?}", in_header);
1175        let ReadIn {
1176            fh, offset, size, ..
1177        } = r.read_struct()?;
1178
1179        if size > self.fs.max_buffer_size() {
1180            return reply_error(
1181                io::Error::from_raw_os_error(libc::ENOMEM),
1182                in_header.unique,
1183                w,
1184            );
1185        }
1186
1187        if !w.has_sufficient_buffer(size) {
1188            return reply_error(
1189                io::Error::from_raw_os_error(libc::ENOMEM),
1190                in_header.unique,
1191                w,
1192            );
1193        }
1194
1195        // Skip over enough bytes for the header.
1196        let unique = in_header.unique;
1197        let result = w.write_at(size_of::<OutHeader>(), |cursor| {
1198            match self.fs.readdir(
1199                Context::from(in_header),
1200                in_header.nodeid.into(),
1201                fh.into(),
1202                size,
1203                offset,
1204            ) {
1205                Ok(mut entries) => {
1206                    let mut total_written = 0;
1207                    while let Some(dirent) = entries.next() {
1208                        let mut entry_inode = None;
1209                        let dirent_result = self
1210                            .lookup_dirent_attribute(&in_header, &dirent)
1211                            .and_then(|e| {
1212                                entry_inode = Some(e.inode);
1213                                let remaining = (size as usize).saturating_sub(total_written);
1214                                add_dirent(cursor, remaining, &dirent, Some(e))
1215                            });
1216
1217                        match dirent_result {
1218                            Ok(0) => {
1219                                // No more space left in the buffer but we need to undo the lookup
1220                                // that created the Entry or we will end up with mismatched lookup
1221                                // counts.
1222                                if let Some(inode) = entry_inode {
1223                                    self.fs.forget(Context::from(in_header), inode.into(), 1);
1224                                }
1225                                break;
1226                            }
1227                            Ok(bytes_written) => {
1228                                total_written += bytes_written;
1229                            }
1230                            Err(e) => {
1231                                if let Some(inode) = entry_inode {
1232                                    self.fs.forget(Context::from(in_header), inode.into(), 1);
1233                                }
1234
1235                                if total_written == 0 {
1236                                    // We haven't filled any entries yet so we can just propagate
1237                                    // the error.
1238                                    return Err(e);
1239                                }
1240
1241                                // We already filled in some entries. Returning an error now will
1242                                // cause lookup count mismatches for those entries so just return
1243                                // whatever we already have.
1244                                break;
1245                            }
1246                        }
1247                    }
1248                    Ok(total_written)
1249                }
1250                Err(e) => Err(e),
1251            }
1252        });
1253
1254        match result {
1255            Ok(total_written) => reply_readdir(total_written, unique, w),
1256            Err(e) => reply_error(e, unique, w),
1257        }
1258    }
1259
1260    fn releasedir<R: Reader, W: Writer>(
1261        &self,
1262        in_header: InHeader,
1263        mut r: R,
1264        w: W,
1265    ) -> Result<usize> {
1266        let ReleaseIn { fh, flags, .. } = r.read_struct()?;
1267
1268        match self.fs.releasedir(
1269            Context::from(in_header),
1270            in_header.nodeid.into(),
1271            flags,
1272            fh.into(),
1273        ) {
1274            Ok(()) => reply_ok(None::<u8>, None, in_header.unique, w),
1275            Err(e) => reply_error(e, in_header.unique, w),
1276        }
1277    }
1278
1279    fn fsyncdir<R: Reader, W: Writer>(&self, in_header: InHeader, mut r: R, w: W) -> Result<usize> {
1280        let FsyncIn {
1281            fh, fsync_flags, ..
1282        } = r.read_struct()?;
1283        let datasync = fsync_flags & 0x1 != 0;
1284
1285        match self.fs.fsyncdir(
1286            Context::from(in_header),
1287            in_header.nodeid.into(),
1288            datasync,
1289            fh.into(),
1290        ) {
1291            Ok(()) => reply_ok(None::<u8>, None, in_header.unique, w),
1292            Err(e) => reply_error(e, in_header.unique, w),
1293        }
1294    }
1295
1296    fn getlk<R: Reader, W: Writer>(&self, in_header: InHeader, mut _r: R, w: W) -> Result<usize> {
1297        if let Err(e) = self.fs.getlk() {
1298            reply_error(e, in_header.unique, w)
1299        } else {
1300            Ok(0)
1301        }
1302    }
1303
1304    fn setlk<R: Reader, W: Writer>(&self, in_header: InHeader, mut _r: R, w: W) -> Result<usize> {
1305        if let Err(e) = self.fs.setlk() {
1306            reply_error(e, in_header.unique, w)
1307        } else {
1308            Ok(0)
1309        }
1310    }
1311
1312    fn setlkw<R: Reader, W: Writer>(&self, in_header: InHeader, mut _r: R, w: W) -> Result<usize> {
1313        if let Err(e) = self.fs.setlkw() {
1314            reply_error(e, in_header.unique, w)
1315        } else {
1316            Ok(0)
1317        }
1318    }
1319
1320    fn access<R: Reader, W: Writer>(&self, in_header: InHeader, mut r: R, w: W) -> Result<usize> {
1321        let AccessIn { mask, .. } = r.read_struct()?;
1322
1323        match self
1324            .fs
1325            .access(Context::from(in_header), in_header.nodeid.into(), mask)
1326        {
1327            Ok(()) => reply_ok(None::<u8>, None, in_header.unique, w),
1328            Err(e) => reply_error(e, in_header.unique, w),
1329        }
1330    }
1331
1332    fn create<R: Reader, W: Writer>(&self, in_header: InHeader, mut r: R, w: W) -> Result<usize> {
1333        let CreateIn {
1334            flags, mode, umask, ..
1335        } = r.read_struct()?;
1336
1337        let buflen = (in_header.len as usize)
1338            .checked_sub(size_of::<InHeader>())
1339            .and_then(|l| l.checked_sub(size_of::<CreateIn>()))
1340            .ok_or(Error::InvalidHeaderLength)?;
1341
1342        let mut buf = vec![0; buflen];
1343
1344        r.read_exact(&mut buf).map_err(Error::DecodeMessage)?;
1345
1346        let mut iter = buf.split_inclusive(|&c| c == b'\0');
1347        let name = iter
1348            .next()
1349            .ok_or(Error::MissingParameter)
1350            .and_then(bytes_to_cstr)?;
1351
1352        let split_pos = name.to_bytes_with_nul().len();
1353        let security_ctx = parse_selinux_xattr(&buf[split_pos..])?;
1354
1355        match self.fs.create(
1356            Context::from(in_header),
1357            in_header.nodeid.into(),
1358            name,
1359            mode,
1360            flags,
1361            umask,
1362            security_ctx,
1363        ) {
1364            Ok((entry, handle, opts)) => {
1365                let entry_out = EntryOut {
1366                    nodeid: entry.inode,
1367                    generation: entry.generation,
1368                    entry_valid: entry.entry_timeout.as_secs(),
1369                    attr_valid: entry.attr_timeout.as_secs(),
1370                    entry_valid_nsec: entry.entry_timeout.subsec_nanos(),
1371                    attr_valid_nsec: entry.attr_timeout.subsec_nanos(),
1372                    attr: entry.attr.into(),
1373                };
1374                let open_out = OpenOut {
1375                    fh: handle.map(Into::into).unwrap_or(0),
1376                    open_flags: opts.bits(),
1377                    ..Default::default()
1378                };
1379
1380                // Kind of a hack to write both structs.
1381                reply_ok(
1382                    Some(entry_out),
1383                    Some(open_out.as_bytes()),
1384                    in_header.unique,
1385                    w,
1386                )
1387            }
1388            Err(e) => reply_error(e, in_header.unique, w),
1389        }
1390    }
1391
1392    #[allow(clippy::unnecessary_wraps)]
1393    fn interrupt(&self, _in_header: InHeader) -> Result<usize> {
1394        Ok(0)
1395    }
1396
1397    fn bmap<R: Reader, W: Writer>(&self, in_header: InHeader, mut _r: R, w: W) -> Result<usize> {
1398        if let Err(e) = self.fs.bmap() {
1399            reply_error(e, in_header.unique, w)
1400        } else {
1401            Ok(0)
1402        }
1403    }
1404
1405    #[allow(clippy::unnecessary_wraps)]
1406    fn destroy(&self) -> Result<usize> {
1407        // No reply to this function.
1408        self.fs.destroy();
1409
1410        Ok(0)
1411    }
1412
1413    fn ioctl<R: Reader, W: Writer>(&self, in_header: InHeader, mut r: R, w: W) -> Result<usize> {
1414        let IoctlIn {
1415            fh,
1416            flags,
1417            cmd,
1418            arg,
1419            in_size,
1420            out_size,
1421        } = r.read_struct()?;
1422
1423        let res = self.fs.ioctl(
1424            in_header.into(),
1425            in_header.nodeid.into(),
1426            fh.into(),
1427            IoctlFlags::from_bits_truncate(flags),
1428            cmd,
1429            arg,
1430            in_size,
1431            out_size,
1432            r,
1433        );
1434
1435        match res {
1436            Ok(reply) => match reply {
1437                IoctlReply::Retry { input, output } => {
1438                    retry_ioctl(in_header.unique, input, output, w)
1439                }
1440                IoctlReply::Done(res) => finish_ioctl(in_header.unique, res, w),
1441            },
1442            Err(e) => reply_error(e, in_header.unique, w),
1443        }
1444    }
1445
1446    fn poll<R: Reader, W: Writer>(&self, in_header: InHeader, mut _r: R, w: W) -> Result<usize> {
1447        if let Err(e) = self.fs.poll() {
1448            reply_error(e, in_header.unique, w)
1449        } else {
1450            Ok(0)
1451        }
1452    }
1453
1454    fn notify_reply<R: Reader, W: Writer>(
1455        &self,
1456        in_header: InHeader,
1457        mut _r: R,
1458        w: W,
1459    ) -> Result<usize> {
1460        if let Err(e) = self.fs.notify_reply() {
1461            reply_error(e, in_header.unique, w)
1462        } else {
1463            Ok(0)
1464        }
1465    }
1466
1467    fn batch_forget<R: Reader, W: Writer>(
1468        &self,
1469        in_header: InHeader,
1470        mut r: R,
1471        w: W,
1472    ) -> Result<usize> {
1473        let BatchForgetIn { count, .. } = r.read_struct()?;
1474
1475        if let Some(size) = (count as usize).checked_mul(size_of::<ForgetOne>()) {
1476            if size > self.fs.max_buffer_size() as usize {
1477                return reply_error(
1478                    io::Error::from_raw_os_error(libc::ENOMEM),
1479                    in_header.unique,
1480                    w,
1481                );
1482            }
1483        } else {
1484            return reply_error(
1485                io::Error::from_raw_os_error(libc::EOVERFLOW),
1486                in_header.unique,
1487                w,
1488            );
1489        }
1490
1491        let mut requests = Vec::with_capacity(count as usize);
1492        for _ in 0..count {
1493            let f: ForgetOne = r.read_struct()?;
1494            requests.push((f.nodeid.into(), f.nlookup));
1495        }
1496
1497        self.fs.batch_forget(Context::from(in_header), requests);
1498
1499        // No reply for forget messages.
1500        Ok(0)
1501    }
1502
1503    fn fallocate<R: Reader, W: Writer>(
1504        &self,
1505        in_header: InHeader,
1506        mut r: R,
1507        w: W,
1508    ) -> Result<usize> {
1509        let FallocateIn {
1510            fh,
1511            offset,
1512            length,
1513            mode,
1514            ..
1515        } = r.read_struct()?;
1516
1517        match self.fs.fallocate(
1518            Context::from(in_header),
1519            in_header.nodeid.into(),
1520            fh.into(),
1521            mode,
1522            offset,
1523            length,
1524        ) {
1525            Ok(()) => reply_ok(None::<u8>, None, in_header.unique, w),
1526            Err(e) => reply_error(e, in_header.unique, w),
1527        }
1528    }
1529
1530    fn lseek<R: Reader, W: Writer>(&self, in_header: InHeader, mut _r: R, w: W) -> Result<usize> {
1531        if let Err(e) = self.fs.lseek() {
1532            reply_error(e, in_header.unique, w)
1533        } else {
1534            Ok(0)
1535        }
1536    }
1537
1538    fn copy_file_range<R: Reader, W: Writer>(
1539        &self,
1540        in_header: InHeader,
1541        mut r: R,
1542        w: W,
1543    ) -> Result<usize> {
1544        let CopyFileRangeIn {
1545            fh_src,
1546            off_src,
1547            nodeid_dst,
1548            fh_dst,
1549            off_dst,
1550            len,
1551            flags,
1552        } = r.read_struct()?;
1553
1554        match self.fs.copy_file_range(
1555            Context::from(in_header),
1556            in_header.nodeid.into(),
1557            fh_src.into(),
1558            off_src,
1559            nodeid_dst.into(),
1560            fh_dst.into(),
1561            off_dst,
1562            len,
1563            flags,
1564        ) {
1565            Ok(count) => {
1566                let out = WriteOut {
1567                    size: count as u32,
1568                    ..Default::default()
1569                };
1570
1571                reply_ok(Some(out), None, in_header.unique, w)
1572            }
1573            Err(e) => reply_error(e, in_header.unique, w),
1574        }
1575    }
1576
1577    fn set_up_mapping<R, W, M>(
1578        &self,
1579        in_header: InHeader,
1580        mut r: R,
1581        w: W,
1582        mapper: M,
1583    ) -> Result<usize>
1584    where
1585        R: Reader,
1586        W: Writer,
1587        M: Mapper,
1588    {
1589        let SetUpMappingIn {
1590            fh,
1591            foffset,
1592            len,
1593            flags,
1594            moffset,
1595        } = r.read_struct()?;
1596        let flags = SetUpMappingFlags::from_bits_truncate(flags);
1597
1598        let mut prot = 0;
1599        if flags.contains(SetUpMappingFlags::READ) {
1600            prot |= libc::PROT_READ as u32;
1601        }
1602        if flags.contains(SetUpMappingFlags::WRITE) {
1603            prot |= libc::PROT_WRITE as u32;
1604        }
1605
1606        let size = if let Ok(s) = len.try_into() {
1607            s
1608        } else {
1609            return reply_error(
1610                io::Error::from_raw_os_error(libc::EOVERFLOW),
1611                in_header.unique,
1612                w,
1613            );
1614        };
1615
1616        match self.fs.set_up_mapping(
1617            Context::from(in_header),
1618            in_header.nodeid.into(),
1619            fh.into(),
1620            foffset,
1621            moffset,
1622            size,
1623            prot,
1624            mapper,
1625        ) {
1626            Ok(()) => reply_ok(None::<u8>, None, in_header.unique, w),
1627            Err(e) => {
1628                error!("set_up_mapping failed: {}", e);
1629                reply_error(e, in_header.unique, w)
1630            }
1631        }
1632    }
1633
1634    fn remove_mapping<R, W, M>(
1635        &self,
1636        in_header: InHeader,
1637        mut r: R,
1638        w: W,
1639        mapper: M,
1640    ) -> Result<usize>
1641    where
1642        R: Reader,
1643        W: Writer,
1644        M: Mapper,
1645    {
1646        let RemoveMappingIn { count } = r.read_struct()?;
1647
1648        // `FUSE_REMOVEMAPPING_MAX_ENTRY` is defined as
1649        // `PAGE_SIZE / sizeof(struct fuse_removemapping_one)` in /kernel/include/uapi/linux/fuse.h.
1650        let max_entry = pagesize() / std::mem::size_of::<RemoveMappingOne>();
1651
1652        if max_entry < count as usize {
1653            return reply_error(
1654                io::Error::from_raw_os_error(libc::EINVAL),
1655                in_header.unique,
1656                w,
1657            );
1658        }
1659
1660        let mut msgs = Vec::with_capacity(count as usize);
1661        for _ in 0..(count as usize) {
1662            let msg: RemoveMappingOne = r.read_struct()?;
1663            msgs.push(msg);
1664        }
1665
1666        match self.fs.remove_mapping(&msgs, mapper) {
1667            Ok(()) => reply_ok(None::<u8>, None, in_header.unique, w),
1668            Err(e) => reply_error(e, in_header.unique, w),
1669        }
1670    }
1671
1672    fn open_atomic<R: Reader, W: Writer>(
1673        &self,
1674        in_header: InHeader,
1675        mut r: R,
1676        w: W,
1677    ) -> Result<usize> {
1678        let CreateIn {
1679            flags, mode, umask, ..
1680        } = r.read_struct()?;
1681
1682        let buflen = (in_header.len as usize)
1683            .checked_sub(size_of::<InHeader>())
1684            .and_then(|l| l.checked_sub(size_of::<CreateIn>()))
1685            .ok_or(Error::InvalidHeaderLength)?;
1686
1687        let mut buf = vec![0; buflen];
1688
1689        r.read_exact(&mut buf).map_err(Error::DecodeMessage)?;
1690
1691        let mut iter = buf.split_inclusive(|&c| c == b'\0');
1692        let name = iter
1693            .next()
1694            .ok_or(Error::MissingParameter)
1695            .and_then(bytes_to_cstr)?;
1696
1697        let split_pos = name.to_bytes_with_nul().len();
1698        let security_ctx = parse_selinux_xattr(&buf[split_pos..])?;
1699
1700        match self.fs.atomic_open(
1701            Context::from(in_header),
1702            in_header.nodeid.into(),
1703            name,
1704            mode,
1705            flags,
1706            umask,
1707            security_ctx,
1708        ) {
1709            Ok((entry, handle, opts)) => {
1710                let entry_out = EntryOut {
1711                    nodeid: entry.inode,
1712                    generation: entry.generation,
1713                    entry_valid: entry.entry_timeout.as_secs(),
1714                    attr_valid: entry.attr_timeout.as_secs(),
1715                    entry_valid_nsec: entry.entry_timeout.subsec_nanos(),
1716                    attr_valid_nsec: entry.attr_timeout.subsec_nanos(),
1717                    attr: entry.attr.into(),
1718                };
1719                let open_out = OpenOut {
1720                    fh: handle.map(Into::into).unwrap_or(0),
1721                    open_flags: opts.bits(),
1722                    ..Default::default()
1723                };
1724
1725                // open_out passed the `data` argument, but the two out structs are independent
1726                // This is a hack to return two out stucts in one fuse reply
1727                reply_ok(
1728                    Some(entry_out),
1729                    Some(open_out.as_bytes()),
1730                    in_header.unique,
1731                    w,
1732                )
1733            }
1734            Err(e) => reply_error(e, in_header.unique, w),
1735        }
1736    }
1737}
1738
1739fn retry_ioctl<W: Writer>(
1740    unique: u64,
1741    input: Vec<IoctlIovec>,
1742    output: Vec<IoctlIovec>,
1743    mut w: W,
1744) -> Result<usize> {
1745    // We don't need to check for overflow here because if adding these 2 values caused an overflow
1746    // we would have run out of memory before reaching this point.
1747    if input.len() + output.len() > IOCTL_MAX_IOV {
1748        return Err(Error::TooManyIovecs(
1749            input.len() + output.len(),
1750            IOCTL_MAX_IOV,
1751        ));
1752    }
1753
1754    let len = size_of::<OutHeader>()
1755        + size_of::<IoctlOut>()
1756        + (input.len() * size_of::<IoctlIovec>())
1757        + (output.len() * size_of::<IoctlIovec>());
1758    let header = OutHeader {
1759        len: len as u32,
1760        error: 0,
1761        unique,
1762    };
1763    let out = IoctlOut {
1764        result: 0,
1765        flags: IoctlFlags::RETRY.bits(),
1766        in_iovs: input.len() as u32,
1767        out_iovs: output.len() as u32,
1768    };
1769
1770    let mut total_bytes = size_of::<OutHeader>() + size_of::<IoctlOut>();
1771    w.write_all(header.as_bytes())
1772        .map_err(Error::EncodeMessage)?;
1773    w.write_all(out.as_bytes()).map_err(Error::EncodeMessage)?;
1774    for i in input.into_iter().chain(output.into_iter()) {
1775        total_bytes += i.as_bytes().len();
1776        w.write_all(i.as_bytes()).map_err(Error::EncodeMessage)?;
1777    }
1778
1779    w.flush().map_err(Error::FlushMessage)?;
1780    debug_assert_eq!(len, total_bytes);
1781    Ok(len)
1782}
1783
1784fn finish_ioctl<W: Writer>(unique: u64, res: io::Result<Vec<u8>>, w: W) -> Result<usize> {
1785    let (out, data) = match res {
1786        Ok(data) => {
1787            let out = IoctlOut {
1788                result: 0,
1789                ..Default::default()
1790            };
1791            (out, Some(data))
1792        }
1793        Err(e) => {
1794            let out = IoctlOut {
1795                result: -e.raw_os_error().unwrap_or(libc::EIO),
1796                ..Default::default()
1797            };
1798            (out, None)
1799        }
1800    };
1801    reply_ok(Some(out), data.as_ref().map(|d| &d[..]), unique, w)
1802}
1803
1804fn reply_readdir<W: Writer>(len: usize, unique: u64, mut w: W) -> Result<usize> {
1805    let out = OutHeader {
1806        len: (size_of::<OutHeader>() + len) as u32,
1807        error: 0,
1808        unique,
1809    };
1810
1811    w.write_all(out.as_bytes()).map_err(Error::EncodeMessage)?;
1812    w.flush().map_err(Error::FlushMessage)?;
1813    Ok(out.len as usize)
1814}
1815
1816fn reply_ok<T: IntoBytes + Immutable, W: Writer>(
1817    out: Option<T>,
1818    data: Option<&[u8]>,
1819    unique: u64,
1820    mut w: W,
1821) -> Result<usize> {
1822    let mut len = size_of::<OutHeader>();
1823
1824    if out.is_some() {
1825        len += size_of::<T>();
1826    }
1827
1828    if let Some(data) = data {
1829        len += data.len();
1830    }
1831
1832    let header = OutHeader {
1833        len: len as u32,
1834        error: 0,
1835        unique,
1836    };
1837
1838    let mut total_bytes = size_of::<OutHeader>();
1839    w.write_all(header.as_bytes())
1840        .map_err(Error::EncodeMessage)?;
1841
1842    if let Some(out) = out {
1843        total_bytes += out.as_bytes().len();
1844        w.write_all(out.as_bytes()).map_err(Error::EncodeMessage)?;
1845    }
1846
1847    if let Some(data) = data {
1848        total_bytes += data.len();
1849        w.write_all(data).map_err(Error::EncodeMessage)?;
1850    }
1851
1852    w.flush().map_err(Error::FlushMessage)?;
1853    debug_assert_eq!(len, total_bytes);
1854    Ok(len)
1855}
1856
1857fn reply_error<W: Writer>(e: io::Error, unique: u64, mut w: W) -> Result<usize> {
1858    let header = OutHeader {
1859        len: size_of::<OutHeader>() as u32,
1860        error: -e.raw_os_error().unwrap_or(libc::EIO),
1861        unique,
1862    };
1863
1864    w.write_all(header.as_bytes())
1865        .map_err(Error::EncodeMessage)?;
1866    w.flush().map_err(Error::FlushMessage)?;
1867
1868    Ok(header.len as usize)
1869}
1870
1871fn bytes_to_cstr(buf: &[u8]) -> Result<&CStr> {
1872    // Convert to a `CStr` first so that we can drop the '\0' byte at the end
1873    // and make sure there are no interior '\0' bytes.
1874    CStr::from_bytes_with_nul(buf).map_err(Error::InvalidCString)
1875}
1876
1877fn add_dirent<W: Writer>(
1878    cursor: &mut W,
1879    max: usize,
1880    d: &DirEntry,
1881    entry: Option<Entry>,
1882) -> io::Result<usize> {
1883    // Strip the trailing '\0'.
1884    let name = d.name.to_bytes();
1885    if name.len() > u32::MAX as usize {
1886        return Err(io::Error::from_raw_os_error(libc::EOVERFLOW));
1887    }
1888
1889    let dirent_len = size_of::<Dirent>()
1890        .checked_add(name.len())
1891        .ok_or_else(|| io::Error::from_raw_os_error(libc::EOVERFLOW))?;
1892
1893    // Directory entries must be padded to 8-byte alignment.  If adding 7 causes
1894    // an overflow then this dirent cannot be properly padded.
1895    let padded_dirent_len = dirent_len
1896        .checked_add(7)
1897        .map(|l| l & !7)
1898        .ok_or_else(|| io::Error::from_raw_os_error(libc::EOVERFLOW))?;
1899
1900    let total_len = if entry.is_some() {
1901        padded_dirent_len
1902            .checked_add(size_of::<EntryOut>())
1903            .ok_or_else(|| io::Error::from_raw_os_error(libc::EOVERFLOW))?
1904    } else {
1905        padded_dirent_len
1906    };
1907
1908    if max < total_len {
1909        Ok(0)
1910    } else {
1911        if let Some(entry) = entry {
1912            cursor.write_all(EntryOut::from(entry).as_bytes())?;
1913        }
1914
1915        let dirent = Dirent {
1916            ino: d.ino,
1917            off: d.offset,
1918            namelen: name.len() as u32,
1919            type_: d.type_,
1920        };
1921
1922        cursor.write_all(dirent.as_bytes())?;
1923        cursor.write_all(name)?;
1924
1925        // We know that `dirent_len` <= `padded_dirent_len` due to the check above
1926        // so there's no need for checked arithmetic.
1927        let padding = padded_dirent_len - dirent_len;
1928        if padding > 0 {
1929            cursor.write_all(&DIRENT_PADDING[..padding])?;
1930        }
1931
1932        Ok(total_len)
1933    }
1934}
1935
1936/// Parses the value of the desired attribute from the FUSE request input and returns it as an
1937/// `Ok(CStr)`. Returns `Ok(None)` if `buf` is empty or appears to be a valid request extension that
1938/// does not contain any security context information.
1939///
1940/// # Arguments
1941///
1942/// * `buf` - a byte array that contains the contents following any expected byte string parameters
1943///   of the FUSE request from the server. It begins with a struct `SecctxHeader`, and then the
1944///   subsequent entry is a struct `Secctx` followed by a nul-terminated string with the xattribute
1945///   name and then another nul-terminated string with the value for that xattr.
1946///
1947/// # Errors
1948///
1949/// * `Error::InvalidHeaderLength` - indicates that there is an inconsistency between the size of
1950///   the data read from `buf` and the stated `size` of the `SecctxHeader`, the respective `Secctx`
1951///   struct, or `buf` itself.
1952/// * `Error::DecodeMessage` - indicates that the expected structs cannot be read from `buf`.
1953/// * `Error::MissingParameter` - indicates that either a security context `name` or `value` is
1954///   missing from a security context entry.
1955fn parse_selinux_xattr(buf: &[u8]) -> Result<Option<&CStr>> {
1956    // Return early if request was not followed by context information
1957    if buf.is_empty() {
1958        return Ok(None);
1959    } else if buf.len() < size_of::<SecctxHeader>() {
1960        return Err(Error::InvalidHeaderLength);
1961    }
1962
1963    // Because the security context data block may have been preceded by variable-length strings,
1964    // `SecctxHeader` and the subsequent `Secctx` structs may not be correctly byte-aligned
1965    // within `buf`.
1966    let (secctx_header, _) = SecctxHeader::read_from_prefix(buf)
1967        .map_err(|_| Error::DecodeMessage(io::Error::from_raw_os_error(libc::EINVAL)))?;
1968
1969    // FUSE 7.38 introduced a generic request extension with the same structure as  `SecctxHeader`.
1970    // A `nr_secctx` value above `MAX_NR_SECCTX` indicates that this data block does not contain
1971    // any security context information.
1972    if secctx_header.nr_secctx > MAX_NR_SECCTX {
1973        return Ok(None);
1974    }
1975
1976    let mut cur_secctx_pos = size_of::<SecctxHeader>();
1977    for _ in 0..secctx_header.nr_secctx {
1978        // `SecctxHeader.size` denotes the total size for the `SecctxHeader`, each of the
1979        // `nr_secctx` `Secctx` structs along with the corresponding context name and value,
1980        // and any additional padding.
1981        if (cur_secctx_pos + size_of::<Secctx>()) > buf.len()
1982            || (cur_secctx_pos + size_of::<Secctx>()) > secctx_header.size as usize
1983        {
1984            return Err(Error::InvalidHeaderLength);
1985        }
1986
1987        let secctx =
1988            Secctx::read_from_bytes(&buf[cur_secctx_pos..(cur_secctx_pos + size_of::<Secctx>())])
1989                .map_err(|_| Error::DecodeMessage(io::Error::from_raw_os_error(libc::EINVAL)))?;
1990
1991        cur_secctx_pos += size_of::<Secctx>();
1992
1993        let secctx_data = &buf[cur_secctx_pos..]
1994            .split_inclusive(|&c| c == b'\0')
1995            .take(2)
1996            .map(bytes_to_cstr)
1997            .collect::<Result<Vec<&CStr>>>()?;
1998
1999        if secctx_data.len() != 2 {
2000            return Err(Error::MissingParameter);
2001        }
2002
2003        let name = secctx_data[0];
2004        let value = secctx_data[1];
2005
2006        cur_secctx_pos += name.to_bytes_with_nul().len() + value.to_bytes_with_nul().len();
2007        if cur_secctx_pos > secctx_header.size as usize {
2008            return Err(Error::InvalidHeaderLength);
2009        }
2010
2011        // `Secctx.size` contains the size of the security context value (not including the
2012        // corresponding context name).
2013        if value.to_bytes_with_nul().len() as u32 != secctx.size {
2014            return Err(Error::InvalidHeaderLength);
2015        }
2016
2017        if name.to_bytes_with_nul() == SELINUX_XATTR_CSTR {
2018            return Ok(Some(value));
2019        }
2020    }
2021
2022    // `SecctxHeader.size` is always the total size of the security context data padded to an
2023    // 8-byte alignment. If adding 7 causes an overflow, then the `size` field of our header
2024    // is invalid, so we should return an error.
2025    let padded_secctx_size = cur_secctx_pos
2026        .checked_next_multiple_of(8)
2027        .ok_or(Error::InvalidHeaderLength)?;
2028    if padded_secctx_size != secctx_header.size as usize {
2029        return Err(Error::InvalidHeaderLength);
2030    }
2031
2032    // None of the `nr_secctx` attributes we parsed had a `name` matching `SELINUX_XATTR_CSTR`.
2033    // Return `Ok(None)` to indicate that the security context data block was valid but there was no
2034    // specified selinux label attached to this request.
2035    Ok(None)
2036}
2037
2038#[cfg(test)]
2039mod tests {
2040    use super::*;
2041
2042    fn create_secctx(ctxs: &[(&[u8], &[u8])], size_truncation: u32) -> Vec<u8> {
2043        let nr_secctx = ctxs.len();
2044        let total_size = (size_of::<SecctxHeader>() as u32
2045            + (size_of::<Secctx>() * nr_secctx) as u32
2046            + ctxs
2047                .iter()
2048                .fold(0, |s, &(n, v)| s + n.len() as u32 + v.len() as u32))
2049        .checked_add(7)
2050        .map(|l| l & !7)
2051        .expect("total_size padded to 8-byte boundary")
2052        .checked_sub(size_truncation)
2053        .expect("size truncated by bytes < total_size");
2054
2055        let ctx_data: Vec<_> = ctxs
2056            .iter()
2057            .map(|(n, v)| {
2058                [
2059                    Secctx {
2060                        size: v.len() as u32,
2061                        padding: 0,
2062                    }
2063                    .as_bytes(),
2064                    n,
2065                    v,
2066                ]
2067                .concat()
2068            })
2069            .collect::<Vec<_>>()
2070            .concat();
2071
2072        [
2073            SecctxHeader {
2074                size: total_size,
2075                nr_secctx: nr_secctx as u32,
2076            }
2077            .as_bytes(),
2078            ctx_data.as_slice(),
2079        ]
2080        .concat()
2081    }
2082
2083    #[test]
2084    fn parse_selinux_xattr_empty() {
2085        let v: Vec<u8> = vec![];
2086        let res = parse_selinux_xattr(&v);
2087        assert_eq!(res.unwrap(), None);
2088    }
2089
2090    #[test]
2091    fn parse_selinux_xattr_basic() {
2092        let sec_value = c"user_u:object_r:security_type:s0";
2093        let v = create_secctx(&[(SELINUX_XATTR_CSTR, sec_value.to_bytes_with_nul())], 0);
2094
2095        let res = parse_selinux_xattr(&v);
2096        assert_eq!(res.unwrap(), Some(sec_value));
2097    }
2098
2099    #[test]
2100    fn parse_selinux_xattr_find_attr() {
2101        let foo_value = c"user_foo:object_foo:foo_type:s0";
2102        let sec_value = c"user_u:object_r:security_type:s0";
2103        let v = create_secctx(
2104            &[
2105                (b"foo\0", foo_value.to_bytes_with_nul()),
2106                (SELINUX_XATTR_CSTR, sec_value.to_bytes_with_nul()),
2107            ],
2108            0,
2109        );
2110
2111        let res = parse_selinux_xattr(&v);
2112        assert_eq!(res.unwrap(), Some(sec_value));
2113    }
2114
2115    #[test]
2116    fn parse_selinux_xattr_wrong_attr() {
2117        // Test with an xattr name that looks similar to security.selinux, but has extra
2118        // characters to ensure that `parse_selinux_xattr` will not return the associated
2119        // context value to the caller.
2120        let invalid_selinux_value = c"user_invalid:object_invalid:invalid_type:s0";
2121        let v = create_secctx(
2122            &[(
2123                b"invalid.security.selinux\0",
2124                invalid_selinux_value.to_bytes_with_nul(),
2125            )],
2126            0,
2127        );
2128
2129        let res = parse_selinux_xattr(&v);
2130        assert_eq!(res.unwrap(), None);
2131    }
2132
2133    #[test]
2134    fn parse_selinux_xattr_too_short() {
2135        // Test that parse_selinux_xattr will return an `Error::InvalidHeaderLength` when
2136        // the total size in the `SecctxHeader` does not encompass the entirety of the
2137        // associated data.
2138        let foo_value = c"user_foo:object_foo:foo_type:s0";
2139        let sec_value = c"user_u:object_r:security_type:s0";
2140        let v = create_secctx(
2141            &[
2142                (b"foo\0", foo_value.to_bytes_with_nul()),
2143                (SELINUX_XATTR_CSTR, sec_value.to_bytes_with_nul()),
2144            ],
2145            8,
2146        );
2147
2148        let res = parse_selinux_xattr(&v);
2149        assert!(matches!(res, Err(Error::InvalidHeaderLength)));
2150    }
2151}