def test_chmod(self): self.fs.touch("test.txt") remote_path = fs.path.join(self.test_folder, "test.txt") # Initial permissions info = self.fs.getinfo("test.txt", ["access"]) self.assertEqual(info.permissions.mode, 0o644) st = self.fs.delegate_fs()._sftp.stat(remote_path) self.assertEqual(stat.S_IMODE(st.st_mode), 0o644) # Change permissions with SSHFS._chown self.fs.delegate_fs()._chmod(remote_path, 0o744) info = self.fs.getinfo("test.txt", ["access"]) self.assertEqual(info.permissions.mode, 0o744) st = self.fs.delegate_fs()._sftp.stat(remote_path) self.assertEqual(stat.S_IMODE(st.st_mode), 0o744) # Change permissions with SSHFS.setinfo self.fs.setinfo("test.txt", {"access": { "permissions": Permissions(mode=0o600) }}) info = self.fs.getinfo("test.txt", ["access"]) self.assertEqual(info.permissions.mode, 0o600) st = self.fs.delegate_fs()._sftp.stat(remote_path) self.assertEqual(stat.S_IMODE(st.st_mode), 0o600) with self.assertRaises(fs.errors.PermissionDenied): self.fs.delegate_fs().setinfo( "/", {"access": { "permissions": Permissions(mode=0o777) }})
def test_constructor(self): p = Permissions(names=["foo", "bar"]) self.assertIn("foo", p) self.assertIn("bar", p) self.assertNotIn("baz", p) p = Permissions(user="******", group="w", other="x") self.assertIn("u_r", p) self.assertIn("g_w", p) self.assertIn("o_x", p) self.assertNotIn("sticky", p) self.assertNotIn("setuid", p) self.assertNotIn("setguid", p) p = Permissions(user="******", group="rwx", other="rwx", sticky=True, setuid=True, setguid=True) self.assertIn("sticky", p) self.assertIn("setuid", p) self.assertIn("setguid", p) p = Permissions(mode=0o421) self.assertIn("u_r", p) self.assertIn("g_w", p) self.assertIn("o_x", p) self.assertNotIn("u_w", p) self.assertNotIn("g_x", p) self.assertNotIn("o_r", p) self.assertNotIn("sticky", p) self.assertNotIn("setuid", p) self.assertNotIn("setguid", p)
def test_constructor(self): p = Permissions(names=['foo', 'bar']) self.assertIn('foo', p) self.assertIn('bar', p) self.assertNotIn('baz', p) p = Permissions(user='******', group='w', other='x') self.assertIn('u_r', p) self.assertIn('g_w', p) self.assertIn('o_x', p) self.assertNotIn('sticky', p) self.assertNotIn('setuid', p) self.assertNotIn('setguid', p) p = Permissions(user='******', group='rwx', other='rwx', sticky=True, setuid=True, setguid=True) self.assertIn('sticky', p) self.assertIn('setuid', p) self.assertIn('setguid', p) p = Permissions(mode=0o421) self.assertIn('u_r', p) self.assertIn('g_w', p) self.assertIn('o_x', p) self.assertNotIn('u_w', p) self.assertNotIn('g_x', p) self.assertNotIn('o_r', p) self.assertNotIn('sticky', p) self.assertNotIn('setuid', p) self.assertNotIn('setguid', p)
def getinfo(self, path, namespaces=None): self.check() namespaces = namespaces or () _path = self.validatepath(path) _stat = self._fs.getinfo(_path) info = { "basic": {"name": basename(_path), "is_dir": stat.S_ISDIR(_stat["st_mode"])} } if "details" in namespaces: info["details"] = { "_write": ["accessed", "modified"], "accessed": _stat["st_atime"], "modified": _stat["st_mtime"], "size": _stat["st_size"], "type": int( self.STAT_TO_RESOURCE_TYPE.get( stat.S_IFMT(_stat["st_mode"]), ResourceType.unknown ) ), } if "stat" in namespaces: info["stat"] = _stat if "access" in namespaces: info["access"] = { "permissions": Permissions(mode=_stat["st_mode"]).dump(), "uid": 1000, # TODO: fix "gid": 100, # TODO: fix } return Info(info)
def chmod(self, path: str, mode: oct, recursive: bool = False) -> None: """Change file/directory mode. :param path: Path to be modified. :param mode: Operating-system mode bitfield. Must be in octal's form. Eg: chmod with (mode=0o755) = Permissions(user='******', group='rx', other='rx') :param recursive: If the path is directory, setting recursive to true would change permissions to sub folders and contained files. :type recursive: bool """ assert isinstance(mode, str) or isinstance(mode, int) if isinstance(mode, str): # convert mode to octal mode = int(mode, 8) chmod_cache_info = { "access": { "permissions": Permissions.create(mode) } } if self.isdir(path) and recursive: if path is not "/": self.setinfo(path, chmod_cache_info) # create a walker sub_dir = self.opendir(path) for _path, _ in sub_dir.walk.info(): self.setinfo(path + _path, chmod_cache_info) sub_dir.close() else: self.setinfo(path, chmod_cache_info)
def makedir(self, path, # type: Text permissions=None, # type: Optional[int] recreate=True # type: bool ): # make a directory in the file system. Also, update the cache about the directory. _path = self.norm_path(path) # we always want to overwrite a directory if it already exists. recreate = True if recreate is False else recreate perms = permissions fs_err = None try: super(AbstractFS, self).makedir(_path, permissions=None, recreate=recreate) except fs.errors.FSError as err: fs_err = err finally: if not fs_err: dir_perms = perms if perms else self.default_perms dir_cache = { 'access': { 'permissions': Permissions.create(dir_perms), 'uid': self.default_uid, 'gid': self.default_gid } } logger.debug('Created directory {}'.format(_path)) self.setinfo(_path, info=dir_cache) else: raise fs_err
def chmod(self, path: str, mode: oct, recursive: bool = False) -> None: """Change file/directory mode. :param path: Path to be modified. :param mode: Operating-system mode bitfield. Must be in octal's form. Eg: chmod with (mode=0o755) = Permissions(user='******', group='rx', other='rx') :param recursive: If the path is directory, setting recursive to true would change permissions to sub folders and contained files. :type recursive: bool """ assert (isinstance(mode, str) or isinstance(mode, int)) if isinstance(mode, str): # convert mode to octal mode = int(mode, 8) chmod_cache_info = { 'access': { 'permissions': Permissions.create(mode) } } if self.isdir(path) and recursive: if path is not '/': self.setinfo(path, chmod_cache_info) # create a walker sub_dir = self.opendir(path) for _path, _ in sub_dir.walk.info(): self.setinfo(path + _path, chmod_cache_info) sub_dir.close() else: self.setinfo(path, chmod_cache_info)
def makedir( self, path, # type: Text permissions=None, # type: Optional[int] recreate=True, # type: bool ): # make a directory in the file system. Also, update the cache about the directory. _path = self.norm_path(path) # we always want to overwrite a directory if it already exists. recreate = True if recreate is False else recreate perms = permissions fs_err = None try: super(AbstractFS, self).makedir(_path, permissions=None, recreate=recreate) except fs.errors.FSError as err: fs_err = err finally: if not fs_err: dir_perms = perms if perms else self.default_perms dir_cache = { "access": { "permissions": Permissions.create(dir_perms), "uid": self.default_uid, "gid": self.default_gid, } } logger.debug("Created directory {}".format(_path)) self.setinfo(_path, info=dir_cache) else: raise fs_err
def makedir(self, path, permissions=None, recreate=False): """ Create a directory under `path`. :param str path: Path pointing to a file. :param Permissions permissions: PyFilesystem permission instance :param bool recreate: Not supported """ # type: (Text, Permissions, bool) -> SubFS path = ensureUnicode(path) self.check() _path = toAscii(self.validatepath(path)) if not self.isdir(dirname(_path)): raise errors.ResourceNotFound(path) if permissions is None: permissions = Permissions(user='******', group='r-x', other='r-x') try: self.getinfo(path) except errors.ResourceNotFound: self._odfs.mkdir(_path, permissions.mode) return SubFS(self, path)
def test_create(self): self.assertEqual(Permissions.create(None).mode, 0o777) self.assertEqual(Permissions.create(0o755).mode, 0o755) self.assertEqual(Permissions.create(["u_r", "u_w", "u_x"]).mode, 0o700) self.assertEqual(Permissions.create(Permissions(user="******")).mode, 0o700) with self.assertRaises(ValueError): Permissions.create("foo")
def test_check(self): p = Permissions(user='******') self.assertTrue(p.check('u_r')) self.assertTrue(p.check('u_r', 'u_w')) self.assertTrue(p.check('u_r', 'u_w', 'u_x')) self.assertFalse(p.check('u_r', 'g_w')) self.assertFalse(p.check('g_r', 'g_w')) self.assertFalse(p.check('foo'))
def test_create(self): self.assertEqual(Permissions.create(None).mode, 0o777) self.assertEqual(Permissions.create(0o755).mode, 0o755) self.assertEqual(Permissions.create(['u_r', 'u_w', 'u_x']).mode, 0o700) self.assertEqual( Permissions.create(Permissions(user='******')).mode, 0o700) with self.assertRaises(ValueError): Permissions.create('foo')
def test_check(self): p = Permissions(user="******") self.assertTrue(p.check("u_r")) self.assertTrue(p.check("u_r", "u_w")) self.assertTrue(p.check("u_r", "u_w", "u_x")) self.assertFalse(p.check("u_r", "g_w")) self.assertFalse(p.check("g_r", "g_w")) self.assertFalse(p.check("foo"))
def format_list(self, basedir, listing): """ Return an iterator object that yields the entries of given directory emulating the "/bin/ls -lA" UNIX command output. This is how output should appear: -rw-rw-rw- 1 owner group 7045120 Sep 02 3:47 music.mp3 drwxrwxrwx 1 owner group 0 Aug 31 18:50 e-books -rw-rw-rw- 1 owner group 380 Sep 02 3:40 module.py :param basedir: (str) must be protocol relative path :param listing: (list) list of files to needed for output. """ assert isinstance(basedir, str), basedir basedir += '/' if basedir[-1:] != '/' else basedir now = time.time() for basename in listing: file = self.norm_path( basedir + basename) # for e.g. basedir = '/' and basename = test.png. # So file is '/test.png' try: st = self.stat(file) except (fs.errors.FSError, FilesystemError): raise permission = filemode(Permissions.create(st['st_mode']).mode) if self.isdir(file): permission = permission.replace('?', 'd') elif self.isfile(file): permission = permission.replace('?', '-') elif self.islink(file): permission = permission.replace('?', 'l') nlinks = st['st_nlink'] size = st['st_size'] # file-size uname = self.getinfo(path=file, namespaces=['access']).user # |-> pwd.getpwuid(st['st_uid']).pw_name would fetch the user_name of the actual owner of these files. gname = self.getinfo(path=file, namespaces=['access']).group # |-> grp.getgrgid(st['st_gid']).gr_name would fetch the user_name of the actual of these files. mtime = time.gmtime( fs.time.datetime_to_epoch( self.getinfo(file, namespaces=['details']).modified)) if (now - st['st_mtime']) > (180 * 24 * 60 * 60): fmtstr = "%d %Y" else: fmtstr = "%d %H:%M" mtimestr = "%s %s" % (months_map[mtime.tm_mon], time.strftime(fmtstr, mtime)) if (st['st_mode'] & 61440) == stat.S_IFLNK: # if the file is a symlink, resolve it, e.g. "symlink -> realfile" basename = basename + " -> " + self.readlink(file) # formatting is matched with proftpd ls output line = "%s %3s %-8s %-8s %8s %s %s\r\n" % ( permission, nlinks, uname, gname, size, mtimestr, basename) yield line
def getinfo(self, path: str, get_actual: bool = False, namespaces=None): if get_actual or (not self.built_cache): return self._wrap_fs.getinfo(path, namespaces) else: try: # ensure that the path starts with '/' if path[0] != "/": path = "/" + path info = {"basic": self._cache[path].raw["basic"]} if namespaces is not None: if "details" in namespaces: info["details"] = self._cache[path].raw["details"] if "stat" in namespaces: stat_cache = { "st_uid": self._cache[path].raw["access"]["uid"], "st_gid": self._cache[path].raw["access"]["gid"], "st_atime": self._cache[path].raw["details"]["accessed"], "st_mtime": self._cache[path].raw["details"]["modified"], # TODO: Fix these to appropriate values "st_mtime_ns": None, "st_ctime_ns": None, "st_ctime": None, } if isinstance( self._cache[path].raw["access"]["permissions"], list): stat_cache["st_mode"] = Permissions( self._cache[path].raw["access"] ["permissions"]).mode else: stat_cache["st_mode"] = ( self._cache[path].raw["access"] ["permissions"].mode) self._cache[path].raw["stat"].update(stat_cache) info["stat"] = self._cache[path].raw["stat"] # Note that we won't be keeping tabs on 'lstat' if "lstat" in namespaces: info["lstat"] = self._cache[path].raw["lstat"] info["lstat"] = self._cache[path].raw["lstat"] if "link" in namespaces: info["link"] = self._cache[path].raw["link"] if "access" in namespaces: info["access"] = self._cache[path].raw["access"] return Info(info) except KeyError: raise FilesystemError
def getinfo(self, path: str, get_actual: bool = False, namespaces=None): if get_actual or (not self.built_cache): return self._wrap_fs.getinfo(path, namespaces) else: try: # ensure that the path starts with '/' if path[0] != '/': path = '/' + path info = {'basic': self._cache[path].raw['basic']} if namespaces is not None: if 'details' in namespaces: info['details'] = self._cache[path].raw['details'] if 'stat' in namespaces: stat_cache = { 'st_uid': self._cache[path].raw['access']['uid'], 'st_gid': self._cache[path].raw['access']['gid'], 'st_atime': self._cache[path].raw['details']['accessed'], 'st_mtime': self._cache[path].raw['details']['modified'], # TODO: Fix these to appropriate values 'st_mtime_ns': None, 'st_ctime_ns': None, 'st_ctime': None, } if isinstance( self._cache[path].raw['access']['permissions'], list): stat_cache['st_mode'] = Permissions( self._cache[path].raw['access'] ['permissions']).mode else: stat_cache['st_mode'] = self._cache[path].raw[ 'access']['permissions'].mode self._cache[path].raw['stat'].update(stat_cache) info['stat'] = self._cache[path].raw['stat'] # Note that we won't be keeping tabs on 'lstat' if 'lstat' in namespaces: info['lstat'] = self._cache[path].raw['lstat'] info['lstat'] = self._cache[path].raw['lstat'] if 'link' in namespaces: info['link'] = self._cache[path].raw['link'] if 'access' in namespaces: info['access'] = self._cache[path].raw['access'] return Info(info) except KeyError: raise FilesystemError
def test_properties(self): p = Permissions() self.assertFalse(p.u_r) self.assertNotIn("u_r", p) p.u_r = True self.assertIn("u_r", p) self.assertTrue(p.u_r) p.u_r = False self.assertNotIn("u_r", p) self.assertFalse(p.u_r) self.assertFalse(p.u_w) p.add("u_w") self.assertTrue(p.u_w) p.remove("u_w") self.assertFalse(p.u_w)
def test_repr(self): self.assertEqual(repr(Permissions()), "Permissions(user='', group='', other='')") self.assertEqual(repr(Permissions(names=["foo"])), "Permissions(names=['foo'])") repr(Permissions(user="******", group="rw", other="r")) repr(Permissions(user="******", group="rw", other="r", sticky=True)) repr(Permissions(user="******", group="rw", other="r", setuid=True)) repr(Permissions(user="******", group="rw", other="r", setguid=True))
def makedir(self, path, permissions=None, recreate=False): # noqa: D102 self.check() _permissions = permissions or Permissions(mode=0o755) _path = self.validatepath(path) try: info = self.getinfo(_path) except errors.ResourceNotFound: with self._lock: with convert_sshfs_errors('makedir', path): self._sftp.mkdir(_path, _permissions.mode) else: if (info.is_dir and not recreate) or info.is_file: six.raise_from(errors.DirectoryExists(path), None) return self.opendir(path)
def test_access(self): info = Info({ "access": { "uid": 10, "gid": 12, "user": "******", "group": "devs", "permissions": ["u_r"], } }) self.assertIsInstance(info.permissions, Permissions) self.assertEqual(info.permissions, Permissions(user="******")) self.assertEqual(info.user, "will") self.assertEqual(info.group, "devs") self.assertEqual(info.uid, 10) self.assertEqual(info.gid, 12)
def test_access(self): info = Info({ "access": { "uid": 10, "gid": 12, "user": '******', "group": 'devs', "permissions": ['u_r'] } }) self.assertIsInstance(info.permissions, Permissions) self.assertEqual(info.permissions, Permissions(user='******')) self.assertEqual(info.user, 'will') self.assertEqual(info.group, 'devs') self.assertEqual(info.uid, 10) self.assertEqual(info.gid, 12)
def format_list(self, basedir, listing): """ Return an iterator object that yields the entries of given directory emulating the "/bin/ls -lA" UNIX command output. This is how output should appear: -rw-rw-rw- 1 owner group 7045120 Sep 02 3:47 music.mp3 drwxrwxrwx 1 owner group 0 Aug 31 18:50 e-books -rw-rw-rw- 1 owner group 380 Sep 02 3:40 module.py :param basedir: (str) must be protocol relative path :param listing: (list) list of files to needed for output. """ assert isinstance(basedir, str), basedir basedir += '/' if basedir[-1:] != '/' else basedir now = time.time() for basename in listing: file = self.norm_path(basedir + basename) # for e.g. basedir = '/' and basename = test.png. # So file is '/test.png' try: st = self.stat(file) except (fs.errors.FSError, FilesystemError): raise permission = filemode(Permissions.create(st['st_mode']).mode) if self.isdir(file): permission = permission.replace('?', 'd') elif self.isfile(file): permission = permission.replace('?', '-') elif self.islink(file): permission = permission.replace('?', 'l') nlinks = st['st_nlink'] size = st['st_size'] # file-size uname = self.getinfo(path=file, namespaces=['access']).user # |-> pwd.getpwuid(st['st_uid']).pw_name would fetch the user_name of the actual owner of these files. gname = self.getinfo(path=file, namespaces=['access']).group # |-> grp.getgrgid(st['st_gid']).gr_name would fetch the user_name of the actual of these files. mtime = time.gmtime(fs.time.datetime_to_epoch(self.getinfo(file, namespaces=['details']).modified)) if (now - st['st_mtime']) > (180 * 24 * 60 * 60): fmtstr = "%d %Y" else: fmtstr = "%d %H:%M" mtimestr = "%s %s" % (months_map[mtime.tm_mon], time.strftime(fmtstr, mtime)) if (st['st_mode'] & 61440) == stat.S_IFLNK: # if the file is a symlink, resolve it, e.g. "symlink -> realfile" basename = basename + " -> " + self.readlink(file) # formatting is matched with proftpd ls output line = "%s %3s %-8s %-8s %8s %s %s\r\n" % (permission, nlinks, uname, gname, size, mtimestr, basename) yield line
def test_repr(self): self.assertEqual( repr(Permissions()), "Permissions(user='', group='', other='')", ) self.assertEqual(repr(Permissions(names=['foo'])), "Permissions(names=['foo'])") repr(Permissions(user='******', group='rw', other='r')) repr(Permissions(user='******', group='rw', other='r', sticky=True)) repr(Permissions(user='******', group='rw', other='r', setuid=True)) repr(Permissions(user='******', group='rw', other='r', setguid=True))
def _make_access_from_stat(self, stat_result): """Make an *access* dictionnary from a stat result. """ access = {} access['permissions'] = Permissions(mode=stat_result.st_mode).dump() access['gid'] = stat_result.st_gid access['uid'] = stat_result.st_uid if self.platform in ("linux", "darwin", "freebsd"): def entry_name(db, _id): entry = self._exec_command('getent {} {}'.format(db, _id)) name = next(iter(entry.split(b':'))) return name.decode(self.locale or 'utf-8') access['group'] = entry_name('group', access['gid']) access['user'] = entry_name('passwd', access['uid']) return access
def statToPermissions(attr): """ Convert PyFilesystem Info instance `attr` to permissions string. :param Info attr: The PyFilesystem Info instance. """ # 'other' permissions other = '' other += 'r' if stat.S_IROTH & attr.mode else '-' other += 'w' if stat.S_IWOTH & attr.mode else '-' other += 'x' if stat.S_IXOTH & attr.mode else '-' # 'group' permission group = '' group += 'r' if stat.S_IRGRP & attr.mode else '-' group += 'w' if stat.S_IWGRP & attr.mode else '-' group += 'x' if stat.S_IXGRP & attr.mode else '-' # 'user' permission user = '' user += 'r' if stat.S_IRUSR & attr.mode else '-' user += 'w' if stat.S_IWUSR & attr.mode else '-' user += 'x' if stat.S_IXUSR & attr.mode else '-' sticky = stat.S_ISVTX & attr.mode setuid = stat.S_ISUID & attr.mode setguid = stat.S_ISGID & attr.mode return Permissions(user=user, group=group, other=other, sticky=sticky, setuid=setuid, setguid=setguid)
def test_copy(self): p = Permissions(mode=0o700) p_copy = p.copy() self.assertIsNot(p, p_copy) self.assertEqual(p, p_copy)
def test_mode(self): p = Permissions(user="******", group="rw", other="") self.assertEqual(p.mode, 0o760)
def test_as_str(self): p = Permissions(user="******", group="rwx", other="rwx") self.assertEqual(p.as_str(), "rwxrwxrwx") self.assertEqual(str(p), "rwxrwxrwx") p = Permissions(mode=0o777, setuid=True, setguid=True, sticky=True) self.assertEqual(p.as_str(), "rwsrwsrwt")
def test_make_mode(self): self.assertEqual(make_mode(None), 0o777) self.assertEqual(make_mode(0o755), 0o755) self.assertEqual(make_mode(["u_r", "u_w", "u_x"]), 0o700) self.assertEqual(make_mode(Permissions(user="******")), 0o700)
def test_iter(self): iter_p = iter(Permissions(names=["foo"])) self.assertEqual(list(iter_p), ["foo"])
def test_equality(self): self.assertEqual(Permissions(mode=0o700), Permissions(user="******")) self.assertNotEqual(Permissions(mode=0o500), Permissions(user="******")) self.assertEqual(Permissions(mode=0o700), ["u_r", "u_w", "u_x"])
def test_parse(self): self.assertEqual(Permissions.parse("---------").mode, 0) self.assertEqual(Permissions.parse("rwxrw-r--").mode, 0o764)
def test_mode_set(self): p = Permissions(user="******") self.assertEqual(text_type(p), "r--------") p.mode = 0o700 self.assertEqual(text_type(p), "rwx------")