import os, stat import ctypes, ctypes.util ##from ctypes_configure import configure ##from ctypes_configure.cbuild import ExternalCompilationInfo _libc_name = ctypes.util.find_library('c') _c_lib = ctypes.cdll.LoadLibrary(_libc_name) _openat = _c_lib.openat _openat.argtypes = [ctypes.c_int, ctypes.c_char_p, ctypes.c_int, ctypes.c_int] _openat.restype = ctypes.c_int ##class _CConfig: ## _compilation_info_ = ExternalCompilationInfo( ## pre_include_lines = ['#define _ATFILE_SOURCE'], ## includes = ['sys/types.h', ## 'fcntl.h', ## 'sys/stat.h', ## 'unistd.h']) ## struct_stat = configure.Struct("struct stat", ## [("st_dev", ctypes.c_int), ## ("st_ino", ctypes.c_int)]) ## AT_SYMLINK_NOFOLLOW = configure.ConstantInteger('AT_SYMLINK_NOFOLLOW') ##_cConfig = configure.configure(_CConfig) ##_struct_stat = _cConfig['struct_stat'] ##_AT_SYMLINK_NOFOLLOW = _cConfig['AT_SYMLINK_NOFOLLOW'] ##_fstatat = _c_lib.__fxstatat ##_fstatat.argtypes = [ctypes.c_int, ctypes.c_char_p, ## ctypes.POINTER(_struct_stat), ctypes.c_int] ##_fstatat.restype = ctypes.c_int DEFAULT_FLAGS = (os.O_RDONLY | os.O_LARGEFILE | os.O_NOCTTY | os.O_NONBLOCK) def openat(fd, name, flags=DEFAULT_FLAGS, mode=0): fd = _openat(fd, name, flags, mode) if fd < 0: raise OSError return fd class DirAt(object): __os_close = os.close def __init__(self, fd): self.fd = fd def __del__(self): self.__os_close(self.fd) def join(self, relpath): fd = openat(self.fd, relpath) return DirAt(fd) def parentdir(self): return self.join('..') def listdir(self): # XXX hack return os.listdir('/proc/self/fd/%d' % self.fd) def _key(self): st = os.fstat(self.fd) return (st.st_ino, st.st_dev) def __eq__(self, other): if not isinstance(other, DirAt): return NotImplemented return self._key() == other._key() def __ne__(self, other): if not isinstance(other, DirAt): return NotImplemented return self._key() != other._key() def __hash__(self): return hash(self._key()) def __repr__(self): try: s = repr(str(self)) except OSError: s = '???' return '' % (s,) def __str__(self): parent = self.parentdir() if parent == self: return '/' st1 = os.fstat(self.fd) for name in parent.listdir(): # XXX hack try: st2 = os.lstat('/proc/self/fd/%d/%s' % (parent.fd, name)) except OSError: continue if os.path.samestat(st1, st2): return os.path.join(str(parent), name) else: raise OSError("directory not found in its parent") def opendir(dir): fd = os.open(dir + '/', DEFAULT_FLAGS) return DirAt(fd) def opencwd(): return opendir('.')