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
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
const MAX_NAME_LEN: usize = 255;
@ -143,6 +149,11 @@ impl fuser::Filesystem for FuseClient {
let mut server = self.server.lock();
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
@ -494,6 +505,18 @@ impl fuser::Filesystem for FuseServer {
reply.ok();
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 {
@ -614,8 +637,13 @@ impl FileDescription {
// skip reserved 32 bytes
bytes.advance(32);
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
let last_write_time = bytes.get_u64_le();
// file size
@ -632,6 +660,8 @@ impl FileDescription {
CliprdrError::ConversionFailure
})?;
let from_unix = flags & 0x08 != 0;
let valid_attributes = flags & 0x04 != 0;
if !valid_attributes {
return Err(CliprdrError::InvalidRequest {
@ -641,6 +671,25 @@ impl FileDescription {
// todo: check normal, hidden, system, readonly, archive...
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 {
FileType::Directory
@ -677,7 +726,7 @@ impl FileDescription {
creation_time: last_modified,
size,
perm: PERM_READ,
perm,
};
Ok(desc)
@ -859,7 +908,7 @@ impl FuseNode {
last_metadata_changed: SystemTime::UNIX_EPOCH,
creation_time: SystemTime::UNIX_EPOCH,
size: 0,
perm: PERM_READ,
perm: PERM_RWX,
}
} else {
file.clone()
@ -910,13 +959,14 @@ pub struct InodeAttributes {
last_metadata_changed: std::time::SystemTime,
creation_time: std::time::SystemTime,
kind: FileType,
perm: u16,
// not implemented
_xattrs: BTreeMap<Vec<u8>, Vec<u8>>,
}
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 {
inode,
size,
@ -925,6 +975,7 @@ impl InodeAttributes {
last_metadata_changed: std::time::SystemTime::now(),
creation_time: std::time::SystemTime::now(),
kind,
perm,
_xattrs: BTreeMap::new(),
}
}
@ -938,13 +989,14 @@ impl InodeAttributes {
creation_time: desc.creation_time,
last_accessed: SystemTime::now(),
kind: desc.kind,
perm: desc.perm,
_xattrs: BTreeMap::new(),
}
}
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) {
@ -970,7 +1022,7 @@ impl From<&InodeAttributes> for fuser::FileAttr {
kind: value.kind.into(),
// read only
perm: PERM_READ,
perm: value.perm,
nlink: 1,
// 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::{
bytes::{BufMut, BytesMut},
@ -8,6 +11,18 @@ use utf16string::WString;
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)]
pub(super) struct LocalFile {
pub path: PathBuf,
@ -17,6 +32,7 @@ pub(super) struct LocalFile {
pub size: u64,
pub last_write_time: SystemTime,
pub is_dir: bool,
pub perm: u32,
pub read_only: bool,
pub hidden: bool,
pub system: bool,
@ -34,11 +50,17 @@ impl LocalFile {
let is_dir = mt.is_dir();
let read_only = mt.permissions().readonly();
let system = false;
let hidden = false;
let hidden = if path.to_string_lossy().starts_with('.') {
true
} else {
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 perm = mt.permissions().mode();
let name = path
.display()
.to_string()
@ -66,6 +88,7 @@ impl LocalFile {
read_only,
system,
hidden,
perm,
archive,
normal,
})
@ -109,7 +132,11 @@ impl LocalFile {
&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
buf.put_u32_le(flags);
@ -117,8 +144,16 @@ impl LocalFile {
buf.put(&[0u8; 32][..]);
// file attributes, 4 bytes
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
buf.put_u64_le(win32_time);
// file size (high)
@ -201,6 +236,7 @@ mod file_list_test {
last_write_time: std::time::SystemTime::UNIX_EPOCH,
read_only: false,
is_dir,
perm: 0o754,
hidden: false,
system: false,
archive: false,
@ -263,6 +299,11 @@ mod file_list_test {
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(())
}

View File

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