def ar_open(root, path, flags, mode=None, create=False, truncate=False): """A function similar to os.open() that ensures that the path we're accessing resides within a specified directory subtree. 'root' is a directory that path must reside in. 'path' is a path that is interpreted relative to 'root'. i.e., 'root' is prepended to path. 'path' can not contain any symbolic links that would cause an access to be redirected outside of 'root'. If this happens we'll raise an OSError exception with errno set to EREMOTE 'mode' optional permissions mask used if we create 'path' 'create' optional flag indicating if we should create 'path' 'truncate' optional flag indicating if we should truncate 'path' after opening it.""" # all paths must be absolute assert os.path.isabs(root) # only allow read/write flags assert (flags & ~(os.O_WRONLY | os.O_RDONLY)) == 0 # we can't truncate a file unless we open it for writing assert not truncate or (flags & os.O_WRONLY) # if create is true the user must supply a mode mask assert not create or mode != None # we're going to update root and path so prepare an error # message with the existing values now. eremote = _("Path outside alternate root: root={root}, " "path={path}").format(root=root, path=path) # make target into a relative path if os.path.isabs(path): path = __path_abs_to_relative(path) # now open the alternate root and get its path # done to eliminate any links/mounts/etc in the path root_fd = os.open(root, os.O_RDONLY) try: root = __fd_to_path(root_fd) except OSError as e: if e.errno != errno.ENOENT: os.close(root_fd) raise e os.close(root_fd) # now open the target file, get its path, and make sure it # lives in the alternate root path_fd = None try: path_tmp = os.path.join(root, path) path_fd = os.open(path_tmp, flags) except OSError as e: if e.errno != errno.ENOENT or not create: raise e assert path_fd or create if not path_fd: # the file doesn't exist so we should try to create it. # we'll do this by first opening the directory which # will contain the file and then using openat within # that directory. path_dir = os.path.dirname(path) path_file = os.path.basename(path) try: path_dir_fd = \ ar_open(root, path_dir, os.O_RDONLY) except OSError as e: if e.errno != errno.EREMOTE: raise e raise OSError(errno.EREMOTE, eremote) # we opened the directory, now create the file try: path_fd = sat.openat(path_dir_fd, path_file, flags | os.O_CREAT | os.O_EXCL, mode) except OSError as e: os.close(path_dir_fd) raise e # we created the file assert path_fd os.close(path_dir_fd) # verify that the file we opened lives in the alternate root try: path = __fd_to_path(path_fd) except OSError as e: if e.errno != errno.ENOENT: os.close(path_fd) raise e path = os.path.join(root, path) if not path.startswith(root): os.close(path_fd) raise OSError(errno.EREMOTE, eremote) if truncate: # the user wanted us to truncate the file try: os.ftruncate(path_fd, 0) except OSError as e: os.close(path_fd) raise e return path_fd
# we'll do this by first opening the directory which # will contain the file and then using openat within # that directory. path_dir = os.path.dirname(path) path_file = os.path.basename(path) try: path_dir_fd = \ ar_open(root, path_dir, os.O_RDONLY) except OSError, e: if e.errno != errno.EREMOTE: raise e raise OSError(errno.EREMOTE, eremote) # we opened the directory, now create the file try: path_fd = sat.openat(path_dir_fd, path_file, flags|os.O_CREAT|os.O_EXCL, mode) except OSError, e: os.close(path_dir_fd) raise e # we created the file assert path_fd os.close(path_dir_fd) # verify that the file we opened lives in the alternate root try: path = __fd_to_path(path_fd) except OSError, e: # W0511 XXX / FIXME Comments; pylint: disable=W0511 # XXX: __fd_to_path() can return ENOENT due to 6964121 # pylint: enable=W0511