Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Align backup system to Neovim's system #11374

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
copy xattr
  • Loading branch information
kirawi committed Nov 26, 2024
commit 877054bfaadf0b5f4791c8456c6e01a42f7f10f7
2 changes: 1 addition & 1 deletion helix-loader/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
81 changes: 67 additions & 14 deletions helix-stdx/src/faccess.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)?;
Expand Down Expand Up @@ -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):
Expand Down
34 changes: 30 additions & 4 deletions helix-view/src/document.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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)]
Expand Down Expand Up @@ -1080,6 +1105,7 @@ impl Document {

#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
let mode = from_meta.permissions().mode();
open_opt.mode(mode);
}
Expand Down