def make_tree_read_only(root): """Makes all the files in the directories read only. Also makes the directories read only, only if it makes sense on the platform. This means no file can be created or deleted. """ err = None logging.debug('make_tree_read_only(%s)', root) for dirpath, dirnames, filenames in fs.walk(root, topdown=True): for filename in filenames: e = set_read_only_swallow(os.path.join(dirpath, filename), True) if not err: err = e if sys.platform != 'win32': # It must not be done on Windows. for dirname in dirnames: e = set_read_only_swallow(os.path.join(dirpath, dirname), True) if not err: err = e if sys.platform != 'win32': e = set_read_only_swallow(root, True) if not err: err = e if err: # pylint: disable=raising-bad-type raise err
def make_tree_deleteable(root): """Changes the appropriate permissions so the files in the directories can be deleted. On Windows, the files are modified. On other platforms, modify the directory. It only does the minimum so the files can be deleted safely. Warning on Windows: since file permission is modified, the file node is modified. This means that for hard-linked files, every directory entry for the file node has its file permission modified. """ logging.debug('make_tree_deleteable(%s)', root) err = None if sys.platform != 'win32': e = set_read_only_swallow(root, False) if not err: err = e for dirpath, dirnames, filenames in fs.walk(root, topdown=True): if sys.platform == 'win32': for filename in filenames: e = set_read_only_swallow(os.path.join(dirpath, filename), False) if not err: err = e else: for dirname in dirnames: e = set_read_only_swallow(os.path.join(dirpath, dirname), False) if not err: err = e if err: # pylint: disable=raising-bad-type raise err
def test_symlink_absolute(self): # A symlink to an absolute path is valid. # /dir # /dir/file # /ld -> /dir # /lf -> /ld/file dirpath = os.path.join(self.tempdir, 'dir') filepath = os.path.join(dirpath, 'file') fs.mkdir(dirpath) write_content(filepath, 'hello') linkfile = os.path.join(self.tempdir, 'lf') linkdir = os.path.join(self.tempdir, 'ld') dstfile = os.path.join(linkdir, 'file') fs.symlink(dstfile, linkfile) fs.symlink(dirpath, linkdir) self.assertEqual(True, fs.islink(linkfile)) self.assertEqual(True, fs.islink(linkdir)) self.assertEqual(dstfile, fs.readlink(linkfile)) self.assertEqual(dirpath, fs.readlink(linkdir)) self.assertEqual(['file'], fs.listdir(linkdir)) # /lf resolves to /dir/file. self.assertEqual('hello', fs.open(linkfile).read()) # Ensures that followlinks is respected in walk(). expected = [ (self.tempdir, ['dir', 'ld'], ['lf']), (dirpath, [], ['file']), ] actual = [ (r, sorted(d), sorted(f)) for r, d, f in sorted(fs.walk(self.tempdir, followlinks=False)) ] self.assertEqual(expected, actual) expected = [ (self.tempdir, ['dir', 'ld'], ['lf']), (dirpath, [], ['file']), (linkdir, [], ['file']), ] actual = [ (r, sorted(d), sorted(f)) for r, d, f in sorted(fs.walk(self.tempdir, followlinks=True)) ] self.assertEqual(expected, actual)
def read_tree(path): """Returns a dict with {filepath: content}.""" if not fs.isdir(path): return None out = {} for root, _, filenames in fs.walk(path): for filename in filenames: p = os.path.join(root, filename) out[os.path.relpath(p, path)] = read_file(p) return out
def make_tree_deleteable(root): """Changes the appropriate permissions so the files in the directories can be deleted. On Windows, the files are modified. On other platforms, modify the directory. It only does the minimum so the files can be deleted safely. Warning on Windows: since file permission is modified, the file node is modified. This means that for hard-linked files, every directory entry for the file node has its file permission modified. """ logging.debug('make_tree_deleteable(%s)', root) err = None sudo_failed = False def try_sudo(p): if sys.platform in ('darwin', 'linux2', 'linux') and not sudo_failed: # Try passwordless sudo, just in case. In practice, it is preferable # to use linux capabilities. with open(os.devnull, 'rb') as f: if not subprocess42.call( ['sudo', '-n', 'chmod', 'a+rwX,-t', p], stdin=f): return False logging.debug('sudo chmod %s failed', p) return True if sys.platform != 'win32': e = set_read_only_swallow(root, False) if e: sudo_failed = try_sudo(root) if not err: err = e for dirpath, dirnames, filenames in fs.walk(root, topdown=True): if sys.platform == 'win32': for filename in filenames: e = set_read_only_swallow(os.path.join(dirpath, filename), False) if not err: err = e else: for dirname in dirnames: try: p = os.path.join(dirpath, dirname) except UnicodeDecodeError: logging.error('failed to join "%s" with %s and "%s" with %s', map(ord, dirpath), type(dirpath), map(ord, dirname), type(dirname)) raise e = set_read_only_swallow(p, False) if e: sudo_failed = try_sudo(p) if not err: err = e if err: # pylint: disable=raising-bad-type raise err
def tearDown(self): try: if self._tempdir: for dirpath, dirnames, filenames in fs.walk(self._tempdir, topdown=True): for filename in filenames: file_path.set_read_only(os.path.join(dirpath, filename), False) for dirname in dirnames: file_path.set_read_only(os.path.join(dirpath, dirname), False) file_path.rmtree(self._tempdir) finally: super(FilePathTest, self).tearDown()
def tearDown(self): try: if self._tempdir: for dirpath, dirnames, filenames in fs.walk( self._tempdir, topdown=True): for filename in filenames: file_path.set_read_only(os.path.join(dirpath, filename), False) for dirname in dirnames: file_path.set_read_only(os.path.join(dirpath, dirname), False) file_path.rmtree(self._tempdir) finally: super(FilePathTest, self).tearDown()
def get_recursive_size(path): """Returns the total data size for the specified path.""" try: total = 0 for root, _, files in fs.walk(path): for f in files: total += fs.lstat(os.path.join(root, f)).st_size return total except (IOError, OSError, UnicodeEncodeError) as exc: logging.warning('Exception while getting the size of %s:\n%s', path, exc) # Returns a negative number to make it clear that something is wrong. return -1
def _get_recursive_size(path): """Returns the total data size for the specified path. This function can be surprisingly slow on OSX, so its output should be cached. """ try: total = 0 for root, _, files in fs.walk(path): for f in files: total += fs.lstat(os.path.join(root, f)).st_size return total except (IOError, OSError, UnicodeEncodeError) as exc: logging.warning('Exception while getting the size of %s:\n%s', path, exc) return None
def make_tree_files_read_only(root): """Makes all the files in the directories read only but not the directories themselves. This means files can be created or deleted. """ logging.debug('make_tree_files_read_only(%s)', root) if sys.platform != 'win32': set_read_only(root, False) for dirpath, dirnames, filenames in fs.walk(root, topdown=True): for filename in filenames: set_read_only(os.path.join(dirpath, filename), True) if sys.platform != 'win32': # It must not be done on Windows. for dirname in dirnames: set_read_only(os.path.join(dirpath, dirname), False)
def _get_recursive_size(path): """Returns the total data size for the specified path. This function can be surprisingly slow on OSX, so its output should be cached. """ try: total = 0 if _use_scandir(): if sys.platform == 'win32': def direntIsJunction(entry): # both st_file_attributes and FILE_ATTRIBUTE_REPARSE_POINT are # windows-only symbols. return bool(entry.stat().st_file_attributes & scandir.FILE_ATTRIBUTE_REPARSE_POINT) else: def direntIsJunction(_entry): return False stack = [path] while stack: for entry in scandir.scandir(stack.pop()): if entry.is_symlink() or direntIsJunction(entry): continue if entry.is_file(): total += entry.stat().st_size elif entry.is_dir(): stack.append(entry.path) else: logging.warning('non directory/file entry: %s', entry) return total for root, _, files in fs.walk(path): for f in files: st = fs.lstat(os.path.join(root, f)) if stat.S_ISLNK(st.st_mode): continue total += st.st_size return total except (IOError, OSError, UnicodeEncodeError) as exc: logging.warning('Exception while getting the size of %s:\n%s', path, exc) return None
def make_tree_writeable(root): """Makes all the files in the directories writeable. Also makes the directories writeable, only if it makes sense on the platform. It is different from make_tree_deleteable() because it unconditionally affects the files. """ logging.debug('make_tree_writeable(%s)', root) if sys.platform != 'win32': set_read_only(root, False) for dirpath, dirnames, filenames in fs.walk(root, topdown=True): for filename in filenames: set_read_only(os.path.join(dirpath, filename), False) if sys.platform != 'win32': # It must not be done on Windows. for dirname in dirnames: set_read_only(os.path.join(dirpath, dirname), False)