1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 use log::error;
18 use nix::sys::stat::{mode_t, Mode, SFlag};
19 use std::io;
20 
21 use super::VirtFdService;
22 
23 /// Default/assumed mode of files not created by authfs.
24 ///
25 /// For files that are given to authfs as FDs (i.e. not created through authfs), their mode is
26 /// unknown (or untrusted) until it is ever set. The default mode is just to make it
27 /// readable/writable to VFS. When the mode is set, the value on fd_server is supposed to become
28 /// consistent.
29 const DEFAULT_FILE_MODE: Mode =
30     Mode::from_bits_truncate(Mode::S_IRUSR.bits() | Mode::S_IWUSR.bits());
31 
32 /// Default/assumed mode of directories not created by authfs.
33 ///
34 /// See above.
35 const DEFAULT_DIR_MODE: Mode = Mode::S_IRWXU;
36 
37 /// `Attr` maintains the local truth for attributes (e.g. mode and type) while allowing setting the
38 /// remote attribute for the file description.
39 pub struct Attr {
40     service: VirtFdService,
41     mode: Mode,
42     remote_fd: i32,
43     is_dir: bool,
44 }
45 
46 impl Attr {
new_file(service: VirtFdService, remote_fd: i32) -> Attr47     pub fn new_file(service: VirtFdService, remote_fd: i32) -> Attr {
48         Attr { service, mode: DEFAULT_FILE_MODE, remote_fd, is_dir: false }
49     }
50 
new_dir(service: VirtFdService, remote_fd: i32) -> Attr51     pub fn new_dir(service: VirtFdService, remote_fd: i32) -> Attr {
52         Attr { service, mode: DEFAULT_DIR_MODE, remote_fd, is_dir: true }
53     }
54 
new_file_with_mode(service: VirtFdService, remote_fd: i32, mode: mode_t) -> Attr55     pub fn new_file_with_mode(service: VirtFdService, remote_fd: i32, mode: mode_t) -> Attr {
56         Attr { service, mode: Mode::from_bits_truncate(mode), remote_fd, is_dir: false }
57     }
58 
new_dir_with_mode(service: VirtFdService, remote_fd: i32, mode: mode_t) -> Attr59     pub fn new_dir_with_mode(service: VirtFdService, remote_fd: i32, mode: mode_t) -> Attr {
60         Attr { service, mode: Mode::from_bits_truncate(mode), remote_fd, is_dir: true }
61     }
62 
mode(&self) -> u3263     pub fn mode(&self) -> u32 {
64         self.mode.bits()
65     }
66 
67     /// Sets the file mode.
68     ///
69     /// In addition to the actual file mode, `encoded_mode` also contains information of the file
70     /// type.
set_mode(&mut self, encoded_mode: u32) -> io::Result<()>71     pub fn set_mode(&mut self, encoded_mode: u32) -> io::Result<()> {
72         let new_sflag = SFlag::from_bits_truncate(encoded_mode);
73         let new_mode = Mode::from_bits_truncate(encoded_mode);
74 
75         let type_flag = if self.is_dir { SFlag::S_IFDIR } else { SFlag::S_IFREG };
76         if !type_flag.contains(new_sflag) {
77             return Err(io::Error::from_raw_os_error(libc::EINVAL));
78         }
79 
80         // Request for update only if changing.
81         if new_mode != self.mode {
82             self.service.chmod(self.remote_fd, new_mode.bits() as i32).map_err(|e| {
83                 error!(
84                     "Failed to chmod (fd: {}, mode: {:o}) on fd_server: {:?}",
85                     self.remote_fd, new_mode, e
86                 );
87                 io::Error::from_raw_os_error(libc::EIO)
88             })?;
89             self.mode = new_mode;
90         }
91         Ok(())
92     }
93 }
94