Skip to content

gh-89341: Support creation of a link to the file by fd #136302

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

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

serhiy-storchaka
Copy link
Member

@serhiy-storchaka serhiy-storchaka commented Jul 4, 2025

"test needs fd support in os.link()"
)
@unittest.skipUnless(root_in_posix,
"requires the CAP_DAC_READ_SEARCH capability")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On my Fedora 42, I can use AT_EMPTY_PATH as a regular user. I don't need to be root.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is difficult to check. What getpcaps $$ and cat /proc/$$/status | grep CapEff return to you.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

vstinner@mona$ getpcaps $$
103445: cap_wake_alarm=i
vstinner@mona$ cat /proc/$$/status | grep CapEff
CapEff:	0000000000000000

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does not look like your shell has such capability. Maybe Fedora has patches that ignores it?

@vstinner
Copy link
Member

vstinner commented Jul 8, 2025

My test which works as a normal user on Fedora 42:

import tempfile
import os
import ctypes

TESTNAME = b"linkat_test"

libc = ctypes.cdll.LoadLibrary("libc.so.6")
linkat = libc.linkat
linkat.argtypes = (
    ctypes.c_int,
    ctypes.c_char_p,
    ctypes.c_int,
    ctypes.c_char_p,
    ctypes.c_int,
)
linkat.restype = ctypes.c_int

AT_FDCWD = -100
AT_EMPTY_PATH = 0x1000

def link_file(fd):
    res = linkat(fd, b"",
                 AT_FDCWD, TESTNAME,
                 AT_EMPTY_PATH)
    if res:
        errno = ctypes.get_errno()
        print("linkat() failed: res", res, "errno", errno)
        raise OSError(errno)

try:
    os.unlink(TESTNAME)
except FileNotFoundError:
    pass

fd = os.open(".", os.O_WRONLY | os.O_TMPFILE)
os.write(fd, b"hello world\n")
link_file(fd)
os.close(fd)

with open(TESTNAME) as fp:
    print(fp.read(), end="")

os.unlink(TESTNAME)

@serhiy-storchaka
Copy link
Member Author

It fails on Ubuntu.

linkat() failed: res -1 errno 0
Traceback (most recent call last):
  File "/home/serhiy/py/cpython/t136302.py", line 37, in <module>
    link_file(fd)
    ~~~~~~~~~^^^^
  File "/home/serhiy/py/cpython/t136302.py", line 28, in link_file
    raise OSError(errno)
OSError: 0

@vstinner
Copy link
Member

vstinner commented Jul 8, 2025

I don't know why Fedora behaves differently. Kernel code:

	/*
	 * To use null names we require CAP_DAC_READ_SEARCH or
	 * that the open-time creds of the dfd matches current.
	 * This ensures that not everyone will be able to create
	 * a hardlink using the passed file descriptor.
	 */
	if (flags & AT_EMPTY_PATH)
		how |= LOOKUP_LINKAT_EMPTY;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants