copy xattr

kirawi 2 weeks ago committed by Mason Mac
parent ab66f6e77a
commit 1329572d8d

@ -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…
Cancel
Save