Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
google
GitHub Repository: google/crosvm
Path: blob/main/fuse/src/server.rs
5394 views
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
5
use std::cmp::max;
6
use std::cmp::min;
7
use std::convert::TryInto;
8
use std::ffi::CStr;
9
use std::io;
10
use std::mem::size_of;
11
use std::mem::MaybeUninit;
12
use std::os::unix::io::AsRawFd;
13
use std::time::Duration;
14
15
use base::error;
16
use base::pagesize;
17
use base::Protection;
18
use zerocopy::FromBytes;
19
use zerocopy::Immutable;
20
use zerocopy::IntoBytes;
21
22
use crate::filesystem::Context;
23
use crate::filesystem::DirEntry;
24
use crate::filesystem::DirectoryIterator;
25
use crate::filesystem::Entry;
26
use crate::filesystem::FileSystem;
27
use crate::filesystem::GetxattrReply;
28
use crate::filesystem::IoctlReply;
29
use crate::filesystem::ListxattrReply;
30
use crate::filesystem::ZeroCopyReader;
31
use crate::filesystem::ZeroCopyWriter;
32
use crate::sys::*;
33
use crate::Error;
34
use crate::Result;
35
36
const DIRENT_PADDING: [u8; 8] = [0; 8];
37
38
const SELINUX_XATTR_CSTR: &[u8] = b"security.selinux\0";
39
40
/// A trait for reading from the underlying FUSE endpoint.
41
pub 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
50
impl<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.
56
pub 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
79
impl<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.
100
pub 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
129
impl<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
146
pub struct Server<F: FileSystem + Sync> {
147
fs: F,
148
}
149
150
impl<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
1739
fn 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
1784
fn 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
1804
fn 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
1816
fn 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
1857
fn 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
1871
fn 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
1877
fn 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.
1955
fn 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)]
2039
mod 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
}
2152
2153