feat: extend file list PDU to transfer UNIX PERM

1. used 4 bytes out of a reserved 16 bytes section to store perm u32

2. add FLAGS_FD_UNIX_MODE: u32 = 0x08, used with flags, indicating this
   message is from UNIX peer

Signed-off-by: ClSlaid <cailue@bupt.edu.cn>
This commit is contained in:
ClSlaid 2023-10-20 00:23:16 +08:00
parent 7fbb4045e2
commit fc3187a781
No known key found for this signature in database
GPG Key ID: E0A5F564C51C056E
3 changed files with 106 additions and 14 deletions

View File

@ -50,6 +50,12 @@ const BLOCK_SIZE: u32 = 128 * 1024;
/// read only permission /// read only permission
const PERM_READ: u16 = 0o444; const PERM_READ: u16 = 0o444;
/// read and write permission
const PERM_RW: u16 = 0o644;
/// only self can read and readonly
const PERM_SELF_RO: u16 = 0o400;
/// rwx
const PERM_RWX: u16 = 0o755;
/// max length of file name /// max length of file name
const MAX_NAME_LEN: usize = 255; const MAX_NAME_LEN: usize = 255;
@ -143,6 +149,11 @@ impl fuser::Filesystem for FuseClient {
let mut server = self.server.lock(); let mut server = self.server.lock();
server.release(req, ino, fh, _flags, _lock_owner, _flush, reply) server.release(req, ino, fh, _flags, _lock_owner, _flush, reply)
} }
fn getattr(&mut self, req: &fuser::Request<'_>, ino: u64, reply: fuser::ReplyAttr) {
let mut server = self.server.lock();
server.getattr(req, ino, reply)
}
} }
/// fuse server /// fuse server
@ -494,6 +505,18 @@ impl fuser::Filesystem for FuseServer {
reply.ok(); reply.ok();
return; return;
} }
fn getattr(&mut self, _req: &fuser::Request<'_>, ino: u64, reply: fuser::ReplyAttr) {
let files = &self.files;
let Some(entry) = files.get(ino as usize - 1) else {
reply.error(libc::ENOENT);
log::error!("fuse: getattr: entry not found");
return;
};
let attr = (&entry.attributes).into();
reply.attr(&std::time::Duration::default(), &attr)
}
} }
impl FuseServer { impl FuseServer {
@ -614,8 +637,13 @@ impl FileDescription {
// skip reserved 32 bytes // skip reserved 32 bytes
bytes.advance(32); bytes.advance(32);
let attributes = bytes.get_u32_le(); let attributes = bytes.get_u32_le();
// skip reserved 16 bytes
bytes.advance(16); // in original specification, this is 16 bytes reserved
// we use the last 4 bytes to store the file mode
// skip reserved 12 bytes
bytes.advance(12);
let perm = bytes.get_u32_le() as u16;
// last write time from 1601-01-01 00:00:00, in 100ns // last write time from 1601-01-01 00:00:00, in 100ns
let last_write_time = bytes.get_u64_le(); let last_write_time = bytes.get_u64_le();
// file size // file size
@ -632,6 +660,8 @@ impl FileDescription {
CliprdrError::ConversionFailure CliprdrError::ConversionFailure
})?; })?;
let from_unix = flags & 0x08 != 0;
let valid_attributes = flags & 0x04 != 0; let valid_attributes = flags & 0x04 != 0;
if !valid_attributes { if !valid_attributes {
return Err(CliprdrError::InvalidRequest { return Err(CliprdrError::InvalidRequest {
@ -641,6 +671,25 @@ impl FileDescription {
// todo: check normal, hidden, system, readonly, archive... // todo: check normal, hidden, system, readonly, archive...
let directory = attributes & 0x10 != 0; let directory = attributes & 0x10 != 0;
let normal = attributes == 0x80;
let hidden = attributes & 0x02 != 0;
let readonly = attributes & 0x01 != 0;
let perm = if from_unix {
// as is
perm
// cannot set as is...
} else if normal {
PERM_RWX
} else if readonly {
PERM_READ
} else if hidden {
PERM_SELF_RO
} else if directory {
PERM_RWX
} else {
PERM_RW
};
let kind = if directory { let kind = if directory {
FileType::Directory FileType::Directory
@ -677,7 +726,7 @@ impl FileDescription {
creation_time: last_modified, creation_time: last_modified,
size, size,
perm: PERM_READ, perm,
}; };
Ok(desc) Ok(desc)
@ -859,7 +908,7 @@ impl FuseNode {
last_metadata_changed: SystemTime::UNIX_EPOCH, last_metadata_changed: SystemTime::UNIX_EPOCH,
creation_time: SystemTime::UNIX_EPOCH, creation_time: SystemTime::UNIX_EPOCH,
size: 0, size: 0,
perm: PERM_READ, perm: PERM_RWX,
} }
} else { } else {
file.clone() file.clone()
@ -910,13 +959,14 @@ pub struct InodeAttributes {
last_metadata_changed: std::time::SystemTime, last_metadata_changed: std::time::SystemTime,
creation_time: std::time::SystemTime, creation_time: std::time::SystemTime,
kind: FileType, kind: FileType,
perm: u16,
// not implemented // not implemented
_xattrs: BTreeMap<Vec<u8>, Vec<u8>>, _xattrs: BTreeMap<Vec<u8>, Vec<u8>>,
} }
impl InodeAttributes { impl InodeAttributes {
pub fn new(inode: u64, size: u64, kind: FileType) -> Self { pub fn new(inode: u64, size: u64, perm: u16, kind: FileType) -> Self {
Self { Self {
inode, inode,
size, size,
@ -925,6 +975,7 @@ impl InodeAttributes {
last_metadata_changed: std::time::SystemTime::now(), last_metadata_changed: std::time::SystemTime::now(),
creation_time: std::time::SystemTime::now(), creation_time: std::time::SystemTime::now(),
kind, kind,
perm,
_xattrs: BTreeMap::new(), _xattrs: BTreeMap::new(),
} }
} }
@ -938,13 +989,14 @@ impl InodeAttributes {
creation_time: desc.creation_time, creation_time: desc.creation_time,
last_accessed: SystemTime::now(), last_accessed: SystemTime::now(),
kind: desc.kind, kind: desc.kind,
perm: desc.perm,
_xattrs: BTreeMap::new(), _xattrs: BTreeMap::new(),
} }
} }
pub fn new_root() -> Self { pub fn new_root() -> Self {
Self::new(FUSE_ROOT_ID, 0, FileType::Directory) Self::new(FUSE_ROOT_ID, 0, PERM_RWX, FileType::Directory)
} }
pub fn access(&mut self) { pub fn access(&mut self) {
@ -970,7 +1022,7 @@ impl From<&InodeAttributes> for fuser::FileAttr {
kind: value.kind.into(), kind: value.kind.into(),
// read only // read only
perm: PERM_READ, perm: value.perm,
nlink: 1, nlink: 1,
// set to current user // set to current user

View File

@ -1,4 +1,7 @@
use std::{collections::HashSet, fs::File, path::PathBuf, time::SystemTime}; use std::{
collections::HashSet, fs::File, os::unix::prelude::PermissionsExt, path::PathBuf,
time::SystemTime,
};
use hbb_common::{ use hbb_common::{
bytes::{BufMut, BytesMut}, bytes::{BufMut, BytesMut},
@ -8,6 +11,18 @@ use utf16string::WString;
use crate::{platform::LDAP_EPOCH_DELTA, CliprdrError}; use crate::{platform::LDAP_EPOCH_DELTA, CliprdrError};
/// has valid file attributes
const FLAGS_FD_ATTRIBUTES: u32 = 0x04;
/// has valid file size
const FLAGS_FD_SIZE: u32 = 0x40;
/// has valid last write time
const FLAGS_FD_LAST_WRITE: u32 = 0x20;
/// show progress
const FLAGS_FD_PROGRESSUI: u32 = 0x4000;
/// transferred from unix, contains file mode
/// P.S. this flag is not used in windows
const FLAGS_FD_UNIX_MODE: u32 = 0x08;
#[derive(Debug)] #[derive(Debug)]
pub(super) struct LocalFile { pub(super) struct LocalFile {
pub path: PathBuf, pub path: PathBuf,
@ -17,6 +32,7 @@ pub(super) struct LocalFile {
pub size: u64, pub size: u64,
pub last_write_time: SystemTime, pub last_write_time: SystemTime,
pub is_dir: bool, pub is_dir: bool,
pub perm: u32,
pub read_only: bool, pub read_only: bool,
pub hidden: bool, pub hidden: bool,
pub system: bool, pub system: bool,
@ -34,11 +50,17 @@ impl LocalFile {
let is_dir = mt.is_dir(); let is_dir = mt.is_dir();
let read_only = mt.permissions().readonly(); let read_only = mt.permissions().readonly();
let system = false; let system = false;
let hidden = false; let hidden = if path.to_string_lossy().starts_with('.') {
true
} else {
false
};
let archive = false; let archive = false;
let normal = !is_dir; let normal = !(is_dir || read_only || system || hidden || archive);
let last_write_time = mt.modified().unwrap_or(SystemTime::UNIX_EPOCH); let last_write_time = mt.modified().unwrap_or(SystemTime::UNIX_EPOCH);
let perm = mt.permissions().mode();
let name = path let name = path
.display() .display()
.to_string() .to_string()
@ -66,6 +88,7 @@ impl LocalFile {
read_only, read_only,
system, system,
hidden, hidden,
perm,
archive, archive,
normal, normal,
}) })
@ -109,7 +132,11 @@ impl LocalFile {
&self.name &self.name
); );
let flags = 0x4064; let flags = FLAGS_FD_SIZE
| FLAGS_FD_LAST_WRITE
| FLAGS_FD_ATTRIBUTES
| FLAGS_FD_PROGRESSUI
| FLAGS_FD_UNIX_MODE;
// flags, 4 bytes // flags, 4 bytes
buf.put_u32_le(flags); buf.put_u32_le(flags);
@ -117,8 +144,16 @@ impl LocalFile {
buf.put(&[0u8; 32][..]); buf.put(&[0u8; 32][..]);
// file attributes, 4 bytes // file attributes, 4 bytes
buf.put_u32_le(file_attributes); buf.put_u32_le(file_attributes);
// 16 bytes reserved
buf.put(&[0u8; 16][..]); // NOTE: this is not used in windows
// in the specification, this is 16 bytes reserved
// lets use the last 4 bytes to store the file mode
//
// 12 bytes reserved
buf.put(&[0u8; 12][..]);
// file permissions, 4 bytes
buf.put_u32_le(self.perm);
// last write time, 8 bytes // last write time, 8 bytes
buf.put_u64_le(win32_time); buf.put_u64_le(win32_time);
// file size (high) // file size (high)
@ -201,6 +236,7 @@ mod file_list_test {
last_write_time: std::time::SystemTime::UNIX_EPOCH, last_write_time: std::time::SystemTime::UNIX_EPOCH,
read_only: false, read_only: false,
is_dir, is_dir,
perm: 0o754,
hidden: false, hidden: false,
system: false, system: false,
archive: false, archive: false,
@ -263,6 +299,11 @@ mod file_list_test {
assert_eq!(parsed[3].name.to_str().unwrap(), "b/c.txt"); assert_eq!(parsed[3].name.to_str().unwrap(), "b/c.txt");
} }
assert!(parsed[0].perm & 0o777 == 0o754);
assert!(parsed[1].perm & 0o777 == 0o754);
assert!(parsed[2].perm & 0o777 == 0o754);
assert!(parsed[3].perm & 0o777 == 0o754);
Ok(()) Ok(())
} }

View File

@ -131,7 +131,6 @@ impl ClipboardContext {
let mnt_opts = [ let mnt_opts = [
MountOption::FSName("rustdesk-cliprdr-fs".to_string()), MountOption::FSName("rustdesk-cliprdr-fs".to_string()),
MountOption::RO,
MountOption::NoAtime, MountOption::NoAtime,
]; ];
log::info!( log::info!(