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