class NfSpy(object): options = ( {'mountopt': 'server', 'metavar': 'HOST:PATH', 'help': 'connect to server HOST:PATH'}, {'mountopt': 'hide', 'action': 'store_true', 'help': 'Immediately unmount from the server, staying mounted on the client'}, {'mountopt': 'cachesize', 'type': 'int', 'metavar': 'N', 'default': 1024, 'help': 'Number of handles to cache'}, {'mountopt': 'cachetimeout', 'type': 'int', 'metavar': 'T', 'default': 120, 'help': 'Timeout on handle cache'}, {'mountopt': 'mountport', 'metavar': 'PORT/TRANSPORT', 'default': 'udp', 'help': 'Specify port/transport for mount protocol, e.g. "635/udp"'}, {'mountopt': 'nfsport', 'metavar': 'PORT/TRANSPORT', 'default': 'udp', 'help': 'Specify port/transport for NFS protocol, e.g. "2049/udp"'}, {'mountopt': 'dirhandle', 'metavar': '00:AA:BB...', 'help': 'Use a hex bytes representation of a directory handle instead of using mountd. Colons are ignored.'}, {'mountopt': 'getroot', 'action': 'store_true', 'help': 'Try to find the top-level directory of the export from the directory handle provided with "dirhandle"'}, {'mountopt': 'fakename', 'metavar': 'HOSTNAME', 'help': 'try to fake your hostname'}, ) def __init__(self, server=None, mountport="udp", nfsport="udp", dirhandle=None, hide=False, getroot=False, fakename=None, cachesize=1024, cachetimeout=120): self.authlock = Lock() self.cachetimeout = int(cachetimeout) self.cache = int(cachesize) self.mountport = mountport self.nfsport = nfsport self.mcl = None self.handles = None self.dirhandle = dirhandle self.hide = hide self.getroot = getroot self.server = server self.fakename=fakename def fsinit(self): class FakeUmnt: """ To avoid multiple calls to mountclient.Umnt, set self.mcl = FakeUmnt() """ def Umnt(self, path): pass if self.server: self.host, self.path = self.server.split(':',1); else: raise RuntimeError, "No server specified" if self.dirhandle: self.mcl = FakeUmnt() dh = self.dirhandle.translate(None, ':') self.rootdh = ''.join( chr(int(dh[i:i+2],16)) for i in range(0,len(dh),2) ) else: port, proto = splitport(self.mountport) proto = proto or "udp" try: if proto == "udp": self.mcl = FallbackUDPMountClient(self.host, port) elif proto == "tcp": self.mcl = FallbackTCPMountClient(self.host, port) else: raise RuntimeError, "Invalid mount transport: %s" % proto except socket.error as e: raise RuntimeError, "Problem mounting to %s:%s/%s: %s\n" % ( self.host, repr(port), proto, os.strerror(e.errno)) status, dirhandle, auth_flavors = self.mcl.Mnt(self.path) if status != 0: raise IOError(status, os.strerror(status), self.path) if self.hide: self.mcl.Umnt(self.path) self.mcl = FakeUmnt() self.rootdh = dirhandle port, proto = splitport(self.nfsport) proto = proto or "udp" try: if proto == "udp": self.ncl = EvilFallbackUDPNFSClient(self.host, port,fakename=self.fakename) elif proto == "tcp": self.ncl = EvilFallbackTCPNFSClient(self.host, port,fakename=self.fakename) else: raise RuntimeError, "Invalid NFS transport: %s" % proto except socket.error as e: raise RuntimeError, "Problem establishing NFS to %s:%s/%s: %s\n" % ( self.host, repr(port), proto, os.strerror(e.errno)) self.ncl.fuid = self.ncl.fgid = 0 rest = self.ncl.Fsinfo(self.rootdh) if rest[0]: self.rootattr = rest[0] else: self.rootattr = self.ncl.Getattr(self.rootdh) self.ncl.fuid = self.rootattr[3] self.ncl.fgid = self.rootattr[4] self.rtsize = min(rest[2] or 4096, NFSSVC_MAXBLKSIZE) self.wtsize = min(rest[5] or 4096, NFSSVC_MAXBLKSIZE) self.handles = LRU(self.cache) if self.getroot: try: handle, attr = self.gethandle("/..") while handle != self.rootdh: self.rootdh = handle self.rootattr = attr handle, attr = self.gethandle("/..") except NFSError as e: if e.value != NFSError.NFS3ERR_NOENT: raise def _gethandle(self, path): fh = None fattr = None try: if path == "" or path == "/" or path == "/.." or path == "/.": fh = self.rootdh fattr = self.rootattr else: fh, fattr, cachetime = self.handles[path] # check that it isn't stale self.ncl.fuid = fattr[3] self.ncl.fgid = fattr[4] #Commented to save a call. May cause problems? #fattr = self.ncl.Getattr(dh) #self.handles[path][1] = fattr except (KeyError,NFSError) as e: if isinstance(e, KeyError) or e.errno() == NFSError.NFS3ERR_STALE: if isinstance(e, NFSError): del self.handles[path] tmppath, elem = path.rsplit("/",1) dh, fattr = self.gethandle(tmppath) self.ncl.fuid = fattr[3] self.ncl.fgid = fattr[4] fh, fattr, dattr = self.ncl.Lookup((dh, elem)) self.ncl.fuid = fattr[3] self.ncl.fgid = fattr[4] self.handles[path] = (fh, fattr, time()) self.handles[tmppath] = (dh, dattr, time()) else: raise return (fh, fattr) def gethandle(self, path): if len(self.handles.d) >= self.handles.count: # only prune if cache is full, since prune is O(N) now = time() self.handles.prune(lambda x: now - x[2] > self.cachetimeout) return self._gethandle(path) #'getattr' def getattr(self, path): self.authlock.acquire() try: handle, fattr = self.gethandle(path) fattr = self.ncl.Getattr(handle) self.handles[path] = (handle, fattr, time()) except NFSError as e: no = e.errno() raise IOError(no, os.strerror(no), path) finally: self.authlock.release() st = NFSStat() st.st_mode, st.st_nlink, st.st_uid, st.st_gid, st.st_size \ = fattr[1:6] st.st_atime = fattr[10][0] st.st_mtime = fattr[11][0] st.st_ctime = fattr[12][0] return st #'readlink' def readlink(self, path): if path == "/": return '' self.authlock.acquire() try: handle, fattr = self.gethandle(path) if fattr[0] != NF3LNK: raise IOError(EINVAL, os.strerror(EINVAL), path) fattr, name = self.ncl.Readlink(handle) self.handles[path] = (handle, fattr, time()) except NFSError as e: no = e.errno() raise IOError(no, os.strerror(no), path) finally: self.authlock.release() return name #'readdir' def readdir(self, path, offset): self.authlock.acquire() try: handle, fattr = self.gethandle(path) entries = self.ncl.Listdir(handle, self.rtsize) except NFSError as e: no = e.errno() raise IOError(no, os.strerror(no), path) finally: self.authlock.release() return entries #'mknod' def mknod(self, path, mode, rdev): dirpath, name = path.rsplit('/',1) handle = None fattr = None self.authlock.acquire() try: handle, fattr = self.gethandle(dirpath) if stat.S_ISREG(mode): nh, nattr, wcc = self.ncl.Create( (handle, name, 1, #GUARDED (mode, fattr[3], fattr[4], None, (1,), (1,)) ) ) else: data = None if stat.S_ISCHR(mode): stype = NF3CHR data = (os.major(rdev), os.minor(rdev)) elif stat.S_ISBLK(mode): stype = NF3BLK data = (os.major(rdev), os.minor(rdev)) elif stat.S_ISSOCK(mode): stype = NF3SOCK elif stat.S_ISFIFO(mode): stype = NF3FIFO else: raise IOError(ENOSYS, os.strerror(ENOSYS)) nh, nattr, wcc = self.ncl.Mknod( (handle, name, stype, (mode, fattr[3], fattr[4], None, (1,), (1,) ), data) ) except NFSError as e: no = e.errno() raise IOError(no, os.strerror(no), path) finally: self.authlock.release() now = time() self.handles[path] = (nh, nattr, now) if wcc[1]: self.handles[dirpath] = (handle, wcc[1], now) #'mkdir' def mkdir(self, path, mode): dirpath, name = path.rsplit('/',1) handle = None fattr = None self.authlock.acquire() try: handle, fattr = self.gethandle(dirpath) nh, nattr, wcc = self.ncl.Mkdir( (handle, name, (mode, fattr[3], fattr[4], None, (1,), (1,))) ) except NFSError as e: no = e.errno() raise IOError(no, os.strerror(no), path) finally: self.authlock.release() now = time() self.handles[path] = (nh, nattr, now) if wcc[1]: self.handles[dirpath] = (handle, wcc[1], now) #'unlink' def unlink(self, path): dirpath, name = path.rsplit('/',1) handle = None fattr = None self.authlock.acquire() try: handle, _ = self.gethandle(dirpath) _, fattr = self.gethandle(path) if fattr[0] == NF3DIR: raise IOError(EISDIR, os.strerror(EISDIR), path) wcc = self.ncl.Remove((handle, name)) except NFSError as e: no = e.errno() raise IOError(no, os.strerror(no), path) finally: self.authlock.release() if wcc[1]: self.handles[dirpath] = (handle, wcc[1], time()) #'rmdir' def rmdir(self, path): dirpath, name = path.rsplit('/',1) handle = None fattr = None self.authlock.acquire() try: handle, _ = self.gethandle(dirpath) _, fattr = self.gethandle(path) if fattr[0] != NF3DIR: raise IOError(ENOTDIR, os.strerror(ENOTDIR), path) wcc = self.ncl.Rmdir((handle, name)) except NFSError as e: no = e.errno() raise IOError(no, os.strerror(no), path) finally: self.authlock.release() if wcc[1]: self.handles[dirpath] = (handle, wcc[1], time()) #'symlink' def symlink(self, target, name): dirpath, name = name.rsplit('/',1) handle = None fattr = None self.authlock.acquire() try: handle, _ = self.gethandle(dirpath) nh, nattr, wcc = self.ncl.Symlink((handle, name, (None, self.ncl.fuid, self.ncl.fgid, None, (1,), (1,)), target )) except NFSError as e: no = e.errno() raise IOError(no, os.strerror(no)) finally: self.authlock.release() now = time() self.handles[path] = (nh, nattr, now) if wcc[1]: self.handles[dirpath] = (handle, wcc[1], now) #'rename' def rename(self, old, new): frompath, fromname = old.rsplit('/',1) topath, toname = new.rsplit('/',1) fromhandle = None tohandle = None self.authlock.acquire() try: fromhandle, _ = self.gethandle(frompath) tohandle, _ = self.gethandle(topath) self.gethandle(old) # to get appropriate fuid/fgid try: fwcc, twcc = self.ncl.Rename( (fromhandle, fromname, tohandle, toname)) except NFSError as e: if e.value == NFSError.NFS3ERR_ACCES: self.gethandle(topath) #try different permissions fwcc, twcc = self.ncl.Rename( (fromhandle, fromname, tohandle, toname)) else: raise e except NFSError as e: no = e.errno() raise IOError(no, os.strerror(no)) finally: self.authlock.release() now = time() if fwcc[1]: self.handles[frompath] = (fromhandle, fwcc[1], now) if twcc[1]: self.handles[topath] = (tohandle, twcc[1], now) #'link' def link(self, target, name): dirpath, name = name.rsplit('/',1) fromhandle = None todir = None self.authlock.acquire() try: fromhandle, _ = self.gethandle(target) todir, _ = self.gethandle(dirpath) attr, wcc = self.ncl.Link((fromhandle, todir, name)) except NFSError as e: no = e.errno() raise IOError(no, os.strerror(no)) finally: self.authlock.release() now = time() self.handles[target] = (fromhandle, attr, now) self.handles[name] = (fromhandle, attr, now) if wcc[1]: self.handles[dirpath] = (todir, wcc[1], now) #'chmod' def chmod(self, path, mode): self.authlock.acquire() handle = None fattr = None try: handle, fattr = self.gethandle(path) wcc = self.ncl.Setattr((handle, (mode, None, None, None, (0,), (0,)), None )) except NFSError as e: no = e.errno() raise IOError(no, os.strerror(no)) finally: self.authlock.release() self.handles[path] = (handle, wcc[1] or fattr, time()) #'chown' def chown(self, path, uid, gid): self.authlock.acquire() handle = None fattr = None try: handle, fattr = self.gethandle(path) wcc = self.ncl.Setattr((handle, (None, uid, gid, None, (0,), (0,)), None )) except NFSError as e: no = e.errno() raise IOError(no, os.strerror(no)) finally: self.authlock.release() if wcc[1]: self.handles[path] = (handle, wcc[1], time()) #'truncate' def truncate(self, path, size): self.authlock.acquire() handle = None fattr = None try: handle, fattr = self.gethandle(path) wcc = self.ncl.Setattr((handle, (None, None, None, size, (1,), (1,)), None )) except NFSError as e: no = e.errno() raise IOError(no, os.strerror(no)) finally: self.authlock.release() if wcc[1]: self.handles[path] = (handle, wcc[1], time()) #'utime' def utime(self, path, times): atime, mtime = times self.authlock.acquire() handle = None fattr = None try: handle, fattr = self.gethandle(path) wcc = self.ncl.Setattr((handle, (None, None, None, None, (2,(atime,0)), (2,(mtime,0))), None )) except NFSError as e: no = e.errno() raise IOError(no, os.strerror(no)) finally: self.authlock.release() if wcc[1]: self.handles[path] = (handle, wcc[1], time()) #'open' #'read' def read(self, path, size, offset): if path == "/": raise IOError( EISDIR, os.strerror(EISDIR)) handle = None fattr = None data = None self.authlock.acquire() try: handle, fattr = self.gethandle(path) ret = r'' for chunk in range(offset, offset + size, self.rtsize): fattr, count, eof, data = self.ncl.Read( (handle, chunk, min(self.rtsize,size))) ret += data if eof: break except NFSError as e: no = e.errno() raise IOError(no, os.strerror(no), path) finally: self.authlock.release() self.handles[path] = (handle, fattr, time()) return ret #'write' def write(self, path, buf, offset): self.authlock.acquire() handle = None fattr = None size = 0 wcc = (None,fattr) try: handle, fattr = self.gethandle(path) for chunk in range(offset, offset+len(buf), self.wtsize): length = min(self.wtsize, offset + len(buf) - chunk) base = chunk - offset wcc, count, committed, verf = self.ncl.Write((handle, chunk, length, 2, buf[base:base+length])) size += count except NFSError as e: no = e.errno() raise IOError(no, os.strerror(no), path) finally: self.authlock.release() if wcc[1]: self.handles[path] = (handle, wcc[1], time()) return size #'release' #'statfs' def statfs(self): st = NFSStatvfs() rest = self.ncl.Fsstat(self.rootdh) #XXX: This should be ok, but doesn't actually work? #self.rootattr = rest[0] st.f_frsize = st.f_bsize = self.rtsize st.f_blocks = int(rest[1] // self.rtsize) st.f_bfree = int(rest[2] // self.rtsize) st.f_bavail = int(rest[3] // self.rtsize) st.f_files = rest[4] st.f_ffree = rest[5] st.f_favail = rest[6] return st #'fsync' #'create' #'opendir' #'releasedir' #'fsyncdir' #'flush' #'fgetattr' #'ftruncate' #'getxattr' #'listxattr' #'setxattr' #'removexattr' #'access' def access(self, path, mode): self.authlock.acquire() try: handle, fattr = self.gethandle(path) except NFSError as e: no = e.errno() raise IOError(no, os.strerror(no), path) finally: self.authlock.release() if mode == os.F_OK: return 0 rmode = fattr[1] uid = fattr[3] gid = fattr[4] if uid != 0 and gid != 0: return 0 elif gid != 0: if mode & os.R_OK and rmode & 044: return 0 elif mode & os.W_OK and rmode & 022: return 0 elif mode & os.X_OK and rmode & 011: return 0 else: raise IOError(EACCES, os.strerror(EACCES), path) elif uid != 0: if mode & os.R_OK and rmode & 0404: return 0 elif mode & os.W_OK and rmode & 0202: return 0 elif mode & os.X_OK and rmode & 0101: return 0 else: raise IOError(EACCES, os.strerror(EACCES), path) else: #uid and gid == 0 if mode & os.R_OK and rmode & 4: return 0 elif mode & os.W_OK and rmode & 2: return 0 elif mode & os.X_OK and rmode & 1: return 0 else: raise IOError(EACCES, os.strerror(EACCES), path) #'lock' #'utimens' #'bmap' #'fsinit' #'fsdestroy' def fsdestroy(self): self.mcl.Umnt(self.path)
class NfSpy(object): options = ( { 'mountopt': 'server', 'metavar': 'HOST:PATH', 'help': 'connect to server HOST:PATH' }, { 'mountopt': 'hide', 'action': 'store_true', 'help': 'Immediately unmount from the server, staying mounted on the client' }, { 'mountopt': 'cachesize', 'type': 'int', 'metavar': 'N', 'default': 1024, 'help': 'Number of handles to cache' }, { 'mountopt': 'cachetimeout', 'type': 'int', 'metavar': 'T', 'default': 120, 'help': 'Timeout on handle cache' }, { 'mountopt': 'mountport', 'metavar': 'PORT/TRANSPORT', 'default': 'udp', 'help': 'Specify port/transport for mount protocol, e.g. "635/udp"' }, { 'mountopt': 'nfsport', 'metavar': 'PORT/TRANSPORT', 'default': 'udp', 'help': 'Specify port/transport for NFS protocol, e.g. "2049/udp"' }, { 'mountopt': 'dirhandle', 'metavar': '00:AA:BB...', 'help': 'Use a hex bytes representation of a directory handle instead of using mountd. Colons are ignored.' }, { 'mountopt': 'getroot', 'action': 'store_true', 'help': 'Try to find the top-level directory of the export from the directory handle provided with "dirhandle"' }, { 'mountopt': 'fakename', 'metavar': 'HOSTNAME', 'help': 'try to fake your hostname' }, ) def __init__(self, server=None, mountport="udp", nfsport="udp", dirhandle=None, hide=False, getroot=False, fakename=None, cachesize=1024, cachetimeout=120): self.authlock = Lock() self.cachetimeout = int(cachetimeout) self.cache = int(cachesize) self.mountport = mountport self.nfsport = nfsport self.mcl = None self.handles = None self.dirhandle = dirhandle self.hide = hide self.getroot = getroot self.server = server self.fakename = fakename def fsinit(self): class FakeUmnt: """ To avoid multiple calls to mountclient.Umnt, set self.mcl = FakeUmnt() """ def Umnt(self, path): pass if self.server: self.host, self.path = self.server.split(':', 1) else: raise RuntimeError, "No server specified" if self.dirhandle: self.mcl = FakeUmnt() dh = self.dirhandle.translate(None, ':') self.rootdh = ''.join( chr(int(dh[i:i + 2], 16)) for i in range(0, len(dh), 2)) else: port, proto = splitport(self.mountport) proto = proto or "udp" try: if proto == "udp": self.mcl = FallbackUDPMountClient(self.host, port) elif proto == "tcp": self.mcl = FallbackTCPMountClient(self.host, port) else: raise RuntimeError, "Invalid mount transport: %s" % proto except socket.error as e: raise RuntimeError, "Problem mounting to %s:%s/%s: %s\n" % ( self.host, repr(port), proto, os.strerror(e.errno)) status, dirhandle, auth_flavors = self.mcl.Mnt(self.path) if status != 0: raise IOError(status, os.strerror(status), self.path) if self.hide: self.mcl.Umnt(self.path) self.mcl = FakeUmnt() self.rootdh = dirhandle port, proto = splitport(self.nfsport) proto = proto or "udp" try: if proto == "udp": self.ncl = EvilFallbackUDPNFSClient(self.host, port, fakename=self.fakename) elif proto == "tcp": self.ncl = EvilFallbackTCPNFSClient(self.host, port, fakename=self.fakename) else: raise RuntimeError, "Invalid NFS transport: %s" % proto except socket.error as e: raise RuntimeError, "Problem establishing NFS to %s:%s/%s: %s\n" % ( self.host, repr(port), proto, os.strerror(e.errno)) self.ncl.fuid = self.ncl.fgid = 0 rest = self.ncl.Fsinfo(self.rootdh) if rest[0]: self.rootattr = rest[0] else: self.rootattr = self.ncl.Getattr(self.rootdh) self.ncl.fuid = self.rootattr[3] self.ncl.fgid = self.rootattr[4] self.rtsize = min(rest[2] or 4096, NFSSVC_MAXBLKSIZE) self.wtsize = min(rest[5] or 4096, NFSSVC_MAXBLKSIZE) self.handles = LRU(self.cache) if self.getroot: try: handle, attr = self.gethandle("/..") while handle != self.rootdh: self.rootdh = handle self.rootattr = attr handle, attr = self.gethandle("/..") except NFSError as e: if e.value != NFSError.NFS3ERR_NOENT: raise def _gethandle(self, path): fh = None fattr = None try: if path == "" or path == "/" or path == "/.." or path == "/.": fh = self.rootdh fattr = self.rootattr else: fh, fattr, cachetime = self.handles[path] # check that it isn't stale self.ncl.fuid = fattr[3] self.ncl.fgid = fattr[4] #Commented to save a call. May cause problems? #fattr = self.ncl.Getattr(dh) #self.handles[path][1] = fattr except (KeyError, NFSError) as e: if isinstance(e, KeyError) or e.errno() == NFSError.NFS3ERR_STALE: if isinstance(e, NFSError): del self.handles[path] tmppath, elem = path.rsplit("/", 1) dh, fattr = self.gethandle(tmppath) self.ncl.fuid = fattr[3] self.ncl.fgid = fattr[4] fh, fattr, dattr = self.ncl.Lookup((dh, elem)) self.ncl.fuid = fattr[3] self.ncl.fgid = fattr[4] self.handles[path] = (fh, fattr, time()) self.handles[tmppath] = (dh, dattr, time()) else: raise return (fh, fattr) def gethandle(self, path): if len(self.handles.d) >= self.handles.count: # only prune if cache is full, since prune is O(N) now = time() self.handles.prune(lambda x: now - x[2] > self.cachetimeout) return self._gethandle(path) #'getattr' def getattr(self, path): self.authlock.acquire() try: handle, fattr = self.gethandle(path) fattr = self.ncl.Getattr(handle) self.handles[path] = (handle, fattr, time()) except NFSError as e: no = e.errno() raise IOError(no, os.strerror(no), path) finally: self.authlock.release() st = NFSStat() st.st_mode, st.st_nlink, st.st_uid, st.st_gid, st.st_size \ = fattr[1:6] st.st_atime = fattr[10][0] st.st_mtime = fattr[11][0] st.st_ctime = fattr[12][0] return st #'readlink' def readlink(self, path): if path == "/": return '' self.authlock.acquire() try: handle, fattr = self.gethandle(path) if fattr[0] != NF3LNK: raise IOError(EINVAL, os.strerror(EINVAL), path) fattr, name = self.ncl.Readlink(handle) self.handles[path] = (handle, fattr, time()) except NFSError as e: no = e.errno() raise IOError(no, os.strerror(no), path) finally: self.authlock.release() return name #'readdir' def readdir(self, path, offset): self.authlock.acquire() try: handle, fattr = self.gethandle(path) entries = self.ncl.Listdir(handle, self.rtsize) except NFSError as e: no = e.errno() raise IOError(no, os.strerror(no), path) finally: self.authlock.release() return entries #'mknod' def mknod(self, path, mode, rdev): dirpath, name = path.rsplit('/', 1) handle = None fattr = None self.authlock.acquire() try: handle, fattr = self.gethandle(dirpath) if stat.S_ISREG(mode): nh, nattr, wcc = self.ncl.Create(( handle, name, 1, #GUARDED (mode, fattr[3], fattr[4], None, (1, ), (1, )))) else: data = None if stat.S_ISCHR(mode): stype = NF3CHR data = (os.major(rdev), os.minor(rdev)) elif stat.S_ISBLK(mode): stype = NF3BLK data = (os.major(rdev), os.minor(rdev)) elif stat.S_ISSOCK(mode): stype = NF3SOCK elif stat.S_ISFIFO(mode): stype = NF3FIFO else: raise IOError(ENOSYS, os.strerror(ENOSYS)) nh, nattr, wcc = self.ncl.Mknod( (handle, name, stype, (mode, fattr[3], fattr[4], None, (1, ), (1, )), data)) except NFSError as e: no = e.errno() raise IOError(no, os.strerror(no), path) finally: self.authlock.release() now = time() self.handles[path] = (nh, nattr, now) if wcc[1]: self.handles[dirpath] = (handle, wcc[1], now) #'mkdir' def mkdir(self, path, mode): dirpath, name = path.rsplit('/', 1) handle = None fattr = None self.authlock.acquire() try: handle, fattr = self.gethandle(dirpath) nh, nattr, wcc = self.ncl.Mkdir( (handle, name, (mode, fattr[3], fattr[4], None, (1, ), (1, )))) except NFSError as e: no = e.errno() raise IOError(no, os.strerror(no), path) finally: self.authlock.release() now = time() self.handles[path] = (nh, nattr, now) if wcc[1]: self.handles[dirpath] = (handle, wcc[1], now) #'unlink' def unlink(self, path): dirpath, name = path.rsplit('/', 1) handle = None fattr = None self.authlock.acquire() try: handle, _ = self.gethandle(dirpath) _, fattr = self.gethandle(path) if fattr[0] == NF3DIR: raise IOError(EISDIR, os.strerror(EISDIR), path) wcc = self.ncl.Remove((handle, name)) except NFSError as e: no = e.errno() raise IOError(no, os.strerror(no), path) finally: self.authlock.release() if wcc[1]: self.handles[dirpath] = (handle, wcc[1], time()) #'rmdir' def rmdir(self, path): dirpath, name = path.rsplit('/', 1) handle = None fattr = None self.authlock.acquire() try: handle, _ = self.gethandle(dirpath) _, fattr = self.gethandle(path) if fattr[0] != NF3DIR: raise IOError(ENOTDIR, os.strerror(ENOTDIR), path) wcc = self.ncl.Rmdir((handle, name)) except NFSError as e: no = e.errno() raise IOError(no, os.strerror(no), path) finally: self.authlock.release() if wcc[1]: self.handles[dirpath] = (handle, wcc[1], time()) #'symlink' def symlink(self, target, name): dirpath, name = name.rsplit('/', 1) handle = None fattr = None self.authlock.acquire() try: handle, _ = self.gethandle(dirpath) nh, nattr, wcc = self.ncl.Symlink( (handle, name, (None, self.ncl.fuid, self.ncl.fgid, None, (1, ), (1, )), target)) except NFSError as e: no = e.errno() raise IOError(no, os.strerror(no)) finally: self.authlock.release() now = time() self.handles[path] = (nh, nattr, now) if wcc[1]: self.handles[dirpath] = (handle, wcc[1], now) #'rename' def rename(self, old, new): frompath, fromname = old.rsplit('/', 1) topath, toname = new.rsplit('/', 1) fromhandle = None tohandle = None self.authlock.acquire() try: fromhandle, _ = self.gethandle(frompath) tohandle, _ = self.gethandle(topath) self.gethandle(old) # to get appropriate fuid/fgid try: fwcc, twcc = self.ncl.Rename( (fromhandle, fromname, tohandle, toname)) except NFSError as e: if e.value == NFSError.NFS3ERR_ACCES: self.gethandle(topath) #try different permissions fwcc, twcc = self.ncl.Rename( (fromhandle, fromname, tohandle, toname)) else: raise e except NFSError as e: no = e.errno() raise IOError(no, os.strerror(no)) finally: self.authlock.release() now = time() if fwcc[1]: self.handles[frompath] = (fromhandle, fwcc[1], now) if twcc[1]: self.handles[topath] = (tohandle, twcc[1], now) #'link' def link(self, target, name): dirpath, name = name.rsplit('/', 1) fromhandle = None todir = None self.authlock.acquire() try: fromhandle, _ = self.gethandle(target) todir, _ = self.gethandle(dirpath) attr, wcc = self.ncl.Link((fromhandle, todir, name)) except NFSError as e: no = e.errno() raise IOError(no, os.strerror(no)) finally: self.authlock.release() now = time() self.handles[target] = (fromhandle, attr, now) self.handles[name] = (fromhandle, attr, now) if wcc[1]: self.handles[dirpath] = (todir, wcc[1], now) #'chmod' def chmod(self, path, mode): self.authlock.acquire() handle = None fattr = None try: handle, fattr = self.gethandle(path) wcc = self.ncl.Setattr( (handle, (mode, None, None, None, (0, ), (0, )), None)) except NFSError as e: no = e.errno() raise IOError(no, os.strerror(no)) finally: self.authlock.release() self.handles[path] = (handle, wcc[1] or fattr, time()) #'chown' def chown(self, path, uid, gid): self.authlock.acquire() handle = None fattr = None try: handle, fattr = self.gethandle(path) wcc = self.ncl.Setattr( (handle, (None, uid, gid, None, (0, ), (0, )), None)) except NFSError as e: no = e.errno() raise IOError(no, os.strerror(no)) finally: self.authlock.release() if wcc[1]: self.handles[path] = (handle, wcc[1], time()) #'truncate' def truncate(self, path, size): self.authlock.acquire() handle = None fattr = None try: handle, fattr = self.gethandle(path) wcc = self.ncl.Setattr( (handle, (None, None, None, size, (1, ), (1, )), None)) except NFSError as e: no = e.errno() raise IOError(no, os.strerror(no)) finally: self.authlock.release() if wcc[1]: self.handles[path] = (handle, wcc[1], time()) #'utime' def utime(self, path, times): atime, mtime = times self.authlock.acquire() handle = None fattr = None try: handle, fattr = self.gethandle(path) wcc = self.ncl.Setattr( (handle, (None, None, None, None, (2, (atime, 0)), (2, (mtime, 0))), None)) except NFSError as e: no = e.errno() raise IOError(no, os.strerror(no)) finally: self.authlock.release() if wcc[1]: self.handles[path] = (handle, wcc[1], time()) #'open' #'read' def read(self, path, size, offset): if path == "/": raise IOError(EISDIR, os.strerror(EISDIR)) handle = None fattr = None data = None self.authlock.acquire() try: handle, fattr = self.gethandle(path) ret = r'' for chunk in range(offset, offset + size, self.rtsize): fattr, count, eof, data = self.ncl.Read( (handle, chunk, min(self.rtsize, size))) ret += data if eof: break except NFSError as e: no = e.errno() raise IOError(no, os.strerror(no), path) finally: self.authlock.release() self.handles[path] = (handle, fattr, time()) return ret #'write' def write(self, path, buf, offset): self.authlock.acquire() handle = None fattr = None size = 0 wcc = (None, fattr) try: handle, fattr = self.gethandle(path) for chunk in range(offset, offset + len(buf), self.wtsize): length = min(self.wtsize, offset + len(buf) - chunk) base = chunk - offset wcc, count, committed, verf = self.ncl.Write( (handle, chunk, length, 2, buf[base:base + length])) size += count except NFSError as e: no = e.errno() raise IOError(no, os.strerror(no), path) finally: self.authlock.release() if wcc[1]: self.handles[path] = (handle, wcc[1], time()) return size #'release' #'statfs' def statfs(self): st = NFSStatvfs() rest = self.ncl.Fsstat(self.rootdh) #XXX: This should be ok, but doesn't actually work? #self.rootattr = rest[0] st.f_frsize = st.f_bsize = self.rtsize st.f_blocks = int(rest[1] // self.rtsize) st.f_bfree = int(rest[2] // self.rtsize) st.f_bavail = int(rest[3] // self.rtsize) st.f_files = rest[4] st.f_ffree = rest[5] st.f_favail = rest[6] return st #'fsync' #'create' #'opendir' #'releasedir' #'fsyncdir' #'flush' #'fgetattr' #'ftruncate' #'getxattr' #'listxattr' #'setxattr' #'removexattr' #'access' def access(self, path, mode): self.authlock.acquire() try: handle, fattr = self.gethandle(path) except NFSError as e: no = e.errno() raise IOError(no, os.strerror(no), path) finally: self.authlock.release() if mode == os.F_OK: return 0 rmode = fattr[1] uid = fattr[3] gid = fattr[4] if uid != 0 and gid != 0: return 0 elif gid != 0: if mode & os.R_OK and rmode & 044: return 0 elif mode & os.W_OK and rmode & 022: return 0 elif mode & os.X_OK and rmode & 011: return 0 else: raise IOError(EACCES, os.strerror(EACCES), path) elif uid != 0: if mode & os.R_OK and rmode & 0404: return 0 elif mode & os.W_OK and rmode & 0202: return 0 elif mode & os.X_OK and rmode & 0101: return 0 else: raise IOError(EACCES, os.strerror(EACCES), path) else: #uid and gid == 0 if mode & os.R_OK and rmode & 4: return 0 elif mode & os.W_OK and rmode & 2: return 0 elif mode & os.X_OK and rmode & 1: return 0 else: raise IOError(EACCES, os.strerror(EACCES), path) #'lock' #'utimens' #'bmap' #'fsinit' #'fsdestroy' def fsdestroy(self): self.mcl.Umnt(self.path)