copy xattr
This commit is contained in:
parent
10eedfacc1
commit
877054bfaa
3 changed files with 98 additions and 19 deletions
|
@ -136,7 +136,7 @@ pub fn state_dir() -> PathBuf {
|
|||
#[cfg(unix)]
|
||||
{
|
||||
let strategy = choose_base_strategy().expect("Unable to find the state directory!");
|
||||
let mut path = strategy.state_dir();
|
||||
let mut path = strategy.state_dir().unwrap();
|
||||
path.push("helix");
|
||||
path
|
||||
}
|
||||
|
|
|
@ -54,20 +54,6 @@ mod imp {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn chown(p: &Path, uid: Option<u32>, gid: Option<u32>) -> io::Result<()> {
|
||||
let uid = uid.map(|n| unsafe { rustix::fs::Uid::from_raw(n) });
|
||||
let gid = gid.map(|n| unsafe { rustix::fs::Gid::from_raw(n) });
|
||||
rustix::fs::chown(p, uid, gid)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn fchown(fd: impl AsFd, uid: Option<u32>, gid: Option<u32>) -> io::Result<()> {
|
||||
let uid = uid.map(|n| unsafe { rustix::fs::Uid::from_raw(n) });
|
||||
let gid = gid.map(|n| unsafe { rustix::fs::Gid::from_raw(n) });
|
||||
rustix::fs::fchown(fd, uid, gid)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn copy_metadata(from: &Path, to: &Path) -> io::Result<()> {
|
||||
let from_meta = std::fs::metadata(from)?;
|
||||
let to_meta = std::fs::metadata(to)?;
|
||||
|
@ -547,6 +533,73 @@ pub fn copy_ownership(from: &Path, to: &Path) -> io::Result<()> {
|
|||
imp::copy_ownership(from, to)
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
pub fn chown(p: &Path, uid: Option<u32>, gid: Option<u32>) -> io::Result<()> {
|
||||
let uid = uid.map(|n| unsafe { rustix::fs::Uid::from_raw(n) });
|
||||
let gid = gid.map(|n| unsafe { rustix::fs::Gid::from_raw(n) });
|
||||
rustix::fs::chown(p, uid, gid)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
pub fn fchown(fd: impl std::os::fd::AsFd, uid: Option<u32>, gid: Option<u32>) -> io::Result<()> {
|
||||
let uid = uid.map(|n| unsafe { rustix::fs::Uid::from_raw(n) });
|
||||
let gid = gid.map(|n| unsafe { rustix::fs::Gid::from_raw(n) });
|
||||
rustix::fs::fchown(fd, uid, gid)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
pub fn copy_xattr(from: &Path, to: &Path) -> io::Result<()> {
|
||||
let size = match rustix::fs::listxattr(from, &mut [])? {
|
||||
0 => return Ok(()), // No attributes
|
||||
len => len,
|
||||
};
|
||||
|
||||
let mut buf = vec![0i8; size];
|
||||
let read = rustix::fs::listxattr(from, &mut buf)?;
|
||||
|
||||
fn i8_to_u8_slice(input: &[i8]) -> &[u8] {
|
||||
// SAFETY: Simply reinterprets bytes
|
||||
unsafe { std::slice::from_raw_parts(input.as_ptr() as *const u8, input.len()) }
|
||||
}
|
||||
|
||||
// Iterate over null-terminated C-style strings
|
||||
// Two loops to avoid multiple allocations
|
||||
// Find max-size for attributes
|
||||
let mut max_attr_len = 0;
|
||||
for attr_byte in buf.split(|&b| b == 0) {
|
||||
let name = std::str::from_utf8(i8_to_u8_slice(attr_byte))
|
||||
.map_err(|_| std::io::Error::from(std::io::ErrorKind::InvalidData))?;
|
||||
let attr_len = rustix::fs::getxattr(from, name, &mut [])?;
|
||||
max_attr_len = max_attr_len.max(attr_len);
|
||||
}
|
||||
|
||||
let mut attr_buf = vec![0u8; max_attr_len];
|
||||
for attr_byte in buf.split(|&b| b == 0) {
|
||||
let name = std::str::from_utf8(i8_to_u8_slice(attr_byte))
|
||||
.map_err(|_| std::io::Error::from(std::io::ErrorKind::InvalidData))?;
|
||||
let read = rustix::fs::getxattr(from, name, &mut attr_buf)?;
|
||||
|
||||
// If we can't set xattr because it already exists, try to replace it
|
||||
if read != 0 {
|
||||
match rustix::fs::setxattr(to, name, &attr_buf[..read], rustix::fs::XattrFlags::CREATE)
|
||||
{
|
||||
Err(rustix::io::Errno::EXIST) => rustix::fs::setxattr(
|
||||
to,
|
||||
name,
|
||||
&attr_buf[..read],
|
||||
rustix::fs::XattrFlags::REPLACE,
|
||||
)?,
|
||||
Err(e) => return Err(e.into()),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/*
|
||||
Neovim backup path function:
|
||||
- If a backup is desired (would be disabled by a user if using a file watcher):
|
||||
|
|
|
@ -23,6 +23,7 @@ use std::collections::HashMap;
|
|||
use std::fmt::Display;
|
||||
use std::future::Future;
|
||||
use std::io;
|
||||
use std::os::unix::fs::MetadataExt;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::str::FromStr;
|
||||
use std::sync::{Arc, Weak};
|
||||
|
@ -169,6 +170,27 @@ impl Backup {
|
|||
// builder.permissions()
|
||||
if let Ok(f) = builder.tempfile() {
|
||||
// Check if we have perms to set perms
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use std::os::{fd::AsFd, unix::fs::MetadataExt};
|
||||
|
||||
let from_meta = tokio::fs::metadata(&p).await?;
|
||||
let to_meta = tokio::fs::metadata(&f.path()).await?;
|
||||
let _ = fchown(
|
||||
f.as_file().as_fd(),
|
||||
Some(from_meta.uid()),
|
||||
Some(from_meta.gid()),
|
||||
);
|
||||
|
||||
if from_meta.uid() != to_meta.uid()
|
||||
|| from_meta.gid() != to_meta.gid()
|
||||
|| from_meta.permissions() != to_meta.permissions()
|
||||
{
|
||||
copy = true;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(unix))]
|
||||
if copy_metadata(&p, f.path()).is_err() {
|
||||
copy = true;
|
||||
}
|
||||
|
@ -209,23 +231,26 @@ impl Backup {
|
|||
|
||||
#[cfg(unix)]
|
||||
{
|
||||
let mut meta = tokio::fs::metadata(&p).await?;
|
||||
let mut perms = meta.permissions();
|
||||
use std::os::unix::fs::{MetadataExt, PermissionsExt};
|
||||
|
||||
let mut from_meta = tokio::fs::metadata(&p).await?;
|
||||
let mut perms = from_meta.permissions();
|
||||
|
||||
// Strip s-bit
|
||||
perms.set_mode(perms.mode() & 0o0777);
|
||||
|
||||
let to_meta = tokio::fs::metadata(&backup).await?;
|
||||
let from_gid = from_meta.gid();
|
||||
let to_gid = to_meta.gid();
|
||||
|
||||
// If chown fails, se the protection bits for the roup the same as the perm bits for others
|
||||
if from_gid != to_gid && chown(to, None, Some(from_gid)).is_err() {
|
||||
if from_gid != to_gid && chown(&backup, None, Some(from_gid)).is_err() {
|
||||
let new_perms = (perms.mode() & 0o0707) | ((perms.mode() & 0o07) << 3);
|
||||
perms.set_mode(new_perms);
|
||||
}
|
||||
std::fs::set_permissions(&backup, perms)?;
|
||||
// TODO: Set time
|
||||
// TODO: set xattr via rustix
|
||||
copy_xattr(&p, &backup)?;
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
|
@ -1080,6 +1105,7 @@ impl Document {
|
|||
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
let mode = from_meta.permissions().mode();
|
||||
open_opt.mode(mode);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue