Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,17 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

## [Unreleased] ##

### Breaking ###
* `pathrs_inroot_hardlink` and `pathrs_inroot_symlink` have been switched to
using the standard argument order from their respective system calls
(previously the order was swapped, which lead to possible confusion).
- Previously compiled programs will continue to work but rebuilt programs
will need to adjust their argument order.
- Rust users are not affected by this change.
- For the Go and Python bindings, the wrappers have also had their argument
orders swapped to match the C API and so will also need to be updated when
rebuilding.

## [0.2.2] - 2025-11-25 ##

> 貴様ら全員刀の錆にしてやるぜ。
Expand Down
8 changes: 4 additions & 4 deletions contrib/bindings/python/pathrs/_pathrs.py
Original file line number Diff line number Diff line change
Expand Up @@ -359,7 +359,7 @@ def mknod(self, path: str, mode: int, device: int = 0, /) -> None:
if _is_pathrs_err(err):
raise PathrsError._fetch(err) or INTERNAL_ERROR

def hardlink(self, path: str, target: str, /) -> None:
def hardlink(self, target: str, linkname: str, /) -> None:
"""
Create a hardlink between two paths inside the Root.

Expand All @@ -370,12 +370,12 @@ def hardlink(self, path: str, target: str, /) -> None:
exists.
"""
err = libpathrs_so.pathrs_inroot_hardlink(
self.fileno(), _cstr(path), _cstr(target)
self.fileno(), _cstr(target), _cstr(linkname)
)
if _is_pathrs_err(err):
raise PathrsError._fetch(err) or INTERNAL_ERROR

def symlink(self, path: str, target: str, /) -> None:
def symlink(self, target: str, linkname: str, /) -> None:
"""
Create a symlink at the given path in the Root.

Expand All @@ -387,7 +387,7 @@ def symlink(self, path: str, target: str, /) -> None:
exists.
"""
err = libpathrs_so.pathrs_inroot_symlink(
self.fileno(), _cstr(path), _cstr(target)
self.fileno(), _cstr(target), _cstr(linkname)
)
if _is_pathrs_err(err):
raise PathrsError._fetch(err) or INTERNAL_ERROR
6 changes: 2 additions & 4 deletions e2e-tests/cmd/go/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -314,8 +314,7 @@ var rootHardlinkCmd = &cli.Command{
target := cmd.StringArg("target")
linkname := cmd.StringArg("linkname")

// TODO: These arguments need to get swapped.
return root.Hardlink(linkname, target)
return root.Hardlink(target, linkname)
},
}

Expand All @@ -336,8 +335,7 @@ var rootSymlinkCmd = &cli.Command{
target := cmd.StringArg("target")
linkname := cmd.StringArg("linkname")

// TODO: These arguments need to get swapped.
return root.Symlink(linkname, target)
return root.Symlink(target, linkname)
},
}

Expand Down
6 changes: 2 additions & 4 deletions e2e-tests/cmd/python/pathrs-cmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,17 +99,15 @@ def root_hardlink(args: argparse.Namespace):
target: str = args.target
linkname: str = args.linkname

# TODO: These arguments need to get swapped.
root.hardlink(linkname, target)
root.hardlink(target, linkname)


def root_symlink(args: argparse.Namespace):
root: pathrs.Root = args.root
target: str = args.target
linkname: str = args.linkname

# TODO: These arguments need to get swapped.
root.symlink(linkname, target)
root.symlink(target, linkname)


def root_readlink(args: argparse.Namespace):
Expand Down
16 changes: 8 additions & 8 deletions go-pathrs/internal/libpathrs/libpathrs_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -193,26 +193,26 @@ func InRootMknod(rootFd uintptr, path string, mode uint32, dev uint64) error {
}

// InRootSymlink wraps pathrs_inroot_symlink.
func InRootSymlink(rootFd uintptr, path, target string) error {
cPath := C.CString(path)
defer C.free(unsafe.Pointer(cPath))
func InRootSymlink(rootFd uintptr, target, linkpath string) error {
cLinkpath := C.CString(linkpath)
defer C.free(unsafe.Pointer(cLinkpath))

cTarget := C.CString(target)
defer C.free(unsafe.Pointer(cTarget))

err := C.pathrs_inroot_symlink(C.int(rootFd), cPath, cTarget)
err := C.pathrs_inroot_symlink(C.int(rootFd), cTarget, cLinkpath)
return fetchError(err)
}

// InRootHardlink wraps pathrs_inroot_hardlink.
func InRootHardlink(rootFd uintptr, path, target string) error {
cPath := C.CString(path)
defer C.free(unsafe.Pointer(cPath))
func InRootHardlink(rootFd uintptr, target, linkpath string) error {
cLinkpath := C.CString(linkpath)
defer C.free(unsafe.Pointer(cLinkpath))

cTarget := C.CString(target)
defer C.free(unsafe.Pointer(cTarget))

err := C.pathrs_inroot_hardlink(C.int(rootFd), cPath, cTarget)
err := C.pathrs_inroot_hardlink(C.int(rootFd), cTarget, cLinkpath)
return fetchError(err)
}

Expand Down
12 changes: 6 additions & 6 deletions go-pathrs/root_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -277,26 +277,26 @@ func (r *Root) Mknod(path string, mode os.FileMode, dev uint64) error {
}

// Symlink creates a symlink within a [Root]'s directory tree. The symlink is
// created at path and is a link to target.
// created at newname and is a link to oldname.
//
// This is effectively equivalent to [os.Symlink].
func (r *Root) Symlink(path, target string) error {
func (r *Root) Symlink(oldname, newname string) error {
_, err := fdutils.WithFileFd(r.inner, func(rootFd uintptr) (struct{}, error) {
err := libpathrs.InRootSymlink(rootFd, path, target)
err := libpathrs.InRootSymlink(rootFd, oldname, newname)
return struct{}{}, err
})
return err
}

// Hardlink creates a hardlink within a [Root]'s directory tree. The hardlink
// is created at path and is a link to target. Both paths are within the
// is created at newname and is a link to oldname. Both paths are within the
// [Root]'s directory tree (you cannot hardlink to a different [Root] or the
// host).
//
// This is effectively equivalent to [os.Link].
func (r *Root) Hardlink(path, target string) error {
func (r *Root) Hardlink(oldname, newname string) error {
_, err := fdutils.WithFileFd(r.inner, func(rootFd uintptr) (struct{}, error) {
err := libpathrs.InRootHardlink(rootFd, path, target)
err := libpathrs.InRootHardlink(rootFd, oldname, newname)
return struct{}{}, err
})
return err
Expand Down
8 changes: 6 additions & 2 deletions include/pathrs.h
Original file line number Diff line number Diff line change
Expand Up @@ -520,7 +520,9 @@ int pathrs_inroot_mknod(int root_fd,
* the system errno(7) value associated with the error, etc), use
* pathrs_errorinfo().
*/
int pathrs_inroot_symlink(int root_fd, const char *path, const char *target);
int pathrs_inroot_symlink(int root_fd,
const char *target,
const char *linkpath);

/**
* Create a hardlink within the rootfs referenced by root_fd. Both the hardlink
Expand All @@ -535,7 +537,9 @@ int pathrs_inroot_symlink(int root_fd, const char *path, const char *target);
* the system errno(7) value associated with the error, etc), use
* pathrs_errorinfo().
*/
int pathrs_inroot_hardlink(int root_fd, const char *path, const char *target);
int pathrs_inroot_hardlink(int root_fd,
const char *target,
const char *linkpath);

/**
* Create a new (custom) procfs root handle.
Expand Down
56 changes: 46 additions & 10 deletions src/capi/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -623,24 +623,42 @@
#[no_mangle]
pub unsafe extern "C" fn pathrs_inroot_symlink(
root_fd: CBorrowedFd<'_>,
path: *const c_char,
target: *const c_char,
linkpath: *const c_char,
) -> c_int {
|| -> Result<_, Error> {
let root_fd = root_fd.try_as_borrowed_fd()?;
let root = RootRef::from_fd(root_fd);
let path = unsafe { utils::parse_path(path)? }; // SAFETY: C caller guarantees path is safe.
let target = unsafe { utils::parse_path(target)? }; // SAFETY: C caller guarantees path is safe.
root.create(path, &InodeType::Symlink(target.into()))
let linkpath = unsafe { utils::parse_path(linkpath)? }; // SAFETY: C caller guarantees path is safe.
root.create(linkpath, &InodeType::Symlink(target.into()))
}()
.into_c_return()
}
utils::symver! {
fn pathrs_inroot_symlink <- (pathrs_inroot_symlink, version = "LIBPATHRS_0.2", default);
fn pathrs_inroot_symlink <- (pathrs_inroot_symlink, version = "LIBPATHRS_0.3", default);
}

/// A compatibility shim for `pathrs_inroot_symlink`, which previously took its
/// arguments in the wrong order.
///
/// cbindgen:ignore
#[no_mangle]
pub unsafe extern "C" fn __pathrs_inroot_symlink_v1(
root_fd: CBorrowedFd<'_>,
path: *const c_char,
target: *const c_char,
) -> c_int {
pathrs_inroot_symlink(root_fd, target, path)
}

Check warning on line 653 in src/capi/core.rs

View check run for this annotation

Codecov / codecov/patch

src/capi/core.rs#L647-L653

Added lines #L647 - L653 were not covered by tests
utils::symver! {
// The old version of pathrs_inroot_symlink had "target" and "linkpath"
// flipped.
fn __pathrs_inroot_symlink_v1 <- (pathrs_inroot_symlink, version = "LIBPATHRS_0.2");
// This symbol was renamed in libpathrs 0.2. For backward compatibility with
// pre-symbol-versioned builds of libpathrs, it needs to be a default so
// that loaders will pick it when searching for the unversioned name.
fn pathrs_inroot_symlink <- (pathrs_symlink, version = "LIBPATHRS_0.1", default);
fn __pathrs_inroot_symlink_v1 <- (pathrs_symlink, version = "LIBPATHRS_0.1", default);
}

/// Create a hardlink within the rootfs referenced by root_fd. Both the hardlink
Expand All @@ -657,22 +675,40 @@
#[no_mangle]
pub unsafe extern "C" fn pathrs_inroot_hardlink(
root_fd: CBorrowedFd<'_>,
path: *const c_char,
target: *const c_char,
linkpath: *const c_char,
) -> c_int {
|| -> Result<_, Error> {
let root_fd = root_fd.try_as_borrowed_fd()?;
let root = RootRef::from_fd(root_fd);
let path = unsafe { utils::parse_path(path)? }; // SAFETY: C caller guarantees path is safe.
let target = unsafe { utils::parse_path(target)? }; // SAFETY: C caller guarantees path is safe.
root.create(path, &InodeType::Hardlink(target.into()))
let linkpath = unsafe { utils::parse_path(linkpath)? }; // SAFETY: C caller guarantees path is safe.
root.create(linkpath, &InodeType::Hardlink(target.into()))
}()
.into_c_return()
}
utils::symver! {
fn pathrs_inroot_hardlink <- (pathrs_inroot_hardlink, version = "LIBPATHRS_0.2", default);
fn pathrs_inroot_hardlink <- (pathrs_inroot_hardlink, version = "LIBPATHRS_0.3", default);
}

/// A compatibility shim for `pathrs_inroot_hardlink`, which previously took its
/// arguments in the wrong order.
///
/// cbindgen:ignore
#[no_mangle]
pub unsafe extern "C" fn __pathrs_inroot_hardlink_v1(
root_fd: CBorrowedFd<'_>,
path: *const c_char,
target: *const c_char,
) -> c_int {
pathrs_inroot_hardlink(root_fd, target, path)
}

Check warning on line 705 in src/capi/core.rs

View check run for this annotation

Codecov / codecov/patch

src/capi/core.rs#L699-L705

Added lines #L699 - L705 were not covered by tests
utils::symver! {
// The old version of pathrs_inroot_hardlink had "target" and "linkpath"
// flipped.
fn __pathrs_inroot_hardlink_v1 <- (pathrs_inroot_hardlink, version = "LIBPATHRS_0.2");
// This symbol was renamed in libpathrs 0.2. For backward compatibility with
// pre-symbol-versioned builds of libpathrs, it needs to be a default so
// that loaders will pick it when searching for the unversioned name.
fn pathrs_inroot_hardlink <- (pathrs_hardlink, version = "LIBPATHRS_0.1", default);
fn __pathrs_inroot_hardlink_v1 <- (pathrs_hardlink, version = "LIBPATHRS_0.1", default);
}
4 changes: 2 additions & 2 deletions src/tests/capi/root.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,8 +139,8 @@ impl CapiRoot {
unsafe {
capi::core::pathrs_inroot_symlink(
root_fd.into(),
path.as_ptr(),
target.as_ptr(),
path.as_ptr(),
)
}
}
Expand All @@ -149,8 +149,8 @@ impl CapiRoot {
unsafe {
capi::core::pathrs_inroot_hardlink(
root_fd.into(),
path.as_ptr(),
target.as_ptr(),
path.as_ptr(),
)
}
}
Expand Down
Loading