Skip to content

Instantly share code, notes, and snippets.

@dbnicholson
Created February 12, 2021 20:11
Show Gist options
  • Save dbnicholson/ac5e299a9f18663ba5b11674ee5aaf39 to your computer and use it in GitHub Desktop.
Save dbnicholson/ac5e299a9f18663ba5b11674ee5aaf39 to your computer and use it in GitHub Desktop.
Python renameat2 RENAME_EXCHANGE wrapper
#!/usr/bin/env python3
import ctypes
import errno
import logging
import os
import tempfile
logger = logging.getLogger(os.path.basename(__file__))
libc = ctypes.CDLL('libc.so.6', use_errno=True)
AT_FDCWD = -100
RENAME_EXCHANGE = (1 << 1)
def rename_exchange(a, b):
if hasattr(libc, 'renameat2'):
if libc.renameat2(AT_FDCWD, a, AT_FDCWD, b, RENAME_EXCHANGE) == 0:
logger.debug('renameat2 with RENAME_EXCHANGE succeeded')
return
err = ctypes.get_errno()
if err == errno.EINVAL:
logger.debug('RENAME_EXCHANGE flag not supported by kernel')
elif err == errno.ENOSYS:
logger.debug('renameat2 syscall not available in kernel')
else:
raise OSError(err, os.strerror(err), a, None, b)
else:
logger.debug('renameat2 not available in libc')
# Fallback using a temporary name in the same directory as a. b is
# renamed to the temporary name first so that if they're not on the
# same filesystem no recovery is needed.
logger.debug('Falling back to renames with temporary files')
a_tmp = tempfile.mktemp(prefix=a, dir=os.path.dirname(a))
logger.debug('Renaming "%s" to "%s"', b, a_tmp)
os.rename(b, a_tmp)
logger.debug('Renaming "%s" to "%s"', a, b)
os.rename(a, b)
logger.debug('Renaming "%s" to "%s"', a_tmp, a)
os.rename(a_tmp, a)
if __name__ == '__main__':
from argparse import ArgumentParser
ap = ArgumentParser(description='Try out renameat2 with RENAME_EXCHANGE')
ap.add_argument('A', help='first path')
ap.add_argument('B', help='second path')
ap.add_argument('--debug', dest='log_level', default=logging.WARNING,
action='store_const', const=logging.DEBUG,
help='enable debugging messages')
args = ap.parse_args()
logging.basicConfig(level=args.log_level)
rename_exchange(args.A, args.B)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment