1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
|
# © 2022 Nicko van Someren
# License: MIT
# pylint: disable=missing-module-docstring
from ctypes import CDLL, get_errno
from os import strerror, PathLike, fsencode
from platform import machine
from typing import Optional
SYSCALL_ARCH_MAP = {
'x86_64': 316,
'armv6l': 382,
'armv7l': 382,
'aarch64': 276,
'i386': 353,
}
libc = CDLL('libc.so.6', use_errno=True)
syscall_function = libc.syscall
ARCH = machine()
RENAME_EXCHANGE = 2
AT_FDCWD = -100
if ARCH not in SYSCALL_ARCH_MAP:
raise NotImplementedError(f'Unsupported architecture: {ARCH}')
SYSCALL_RENAMEAT2 = SYSCALL_ARCH_MAP[ARCH]
def rename_exchange(oldpath : PathLike,
newpath : PathLike,
olddirfd : Optional[int] = None,
newdirfd : Optional[int] = None) -> None:
"""Atomically exchange oldpath and newpath. Both pathnames must
exist but may be of different types (e.g., one could be a non-empty
directory and the other a symbolic link)."""
result = syscall_function(
SYSCALL_RENAMEAT2,
olddirfd if olddirfd is not None else AT_FDCWD,
fsencode(oldpath),
newdirfd if newdirfd is not None else AT_FDCWD,
fsencode(newpath),
RENAME_EXCHANGE,
)
if result != 0:
errno = get_errno()
raise OSError(errno, strerror(errno))
|