From 6e641179965ab7750a9a66a2f32de78bbb13d0b5 Mon Sep 17 00:00:00 2001 From: nicholasmhughes Date: Mon, 25 Sep 2023 12:51:58 -0400 Subject: [PATCH] update follow_symlinks functionality in win_file to match file module --- salt/modules/win_file.py | 90 ++++++++++++++++++++++------------------ 1 file changed, 50 insertions(+), 40 deletions(-) diff --git a/salt/modules/win_file.py b/salt/modules/win_file.py index d02d4589f2f..0e8ecf5e883 100644 --- a/salt/modules/win_file.py +++ b/salt/modules/win_file.py @@ -259,7 +259,7 @@ def gid_to_group(gid): salt '*' file.gid_to_group S-1-5-21-626487655-2533044672-482107328-1010 """ - func_name = "{}.gid_to_group".format(__virtualname__) + func_name = f"{__virtualname__}.gid_to_group" if __opts__.get("fun", "") == func_name: log.info( "The function %s should not be used on Windows systems; " @@ -294,7 +294,7 @@ def group_to_gid(group): salt '*' file.group_to_gid administrators """ - func_name = "{}.group_to_gid".format(__virtualname__) + func_name = f"{__virtualname__}.group_to_gid" if __opts__.get("fun", "") == func_name: log.info( "The function %s should not be used on Windows systems; " @@ -336,7 +336,7 @@ def get_pgid(path, follow_symlinks=True): salt '*' file.get_pgid c:\\temp\\test.txt """ if not os.path.exists(path): - raise CommandExecutionError("Path not found: {}".format(path)) + raise CommandExecutionError(f"Path not found: {path}") # Under Windows, if the path is a symlink, the user that owns the symlink is # returned, not the user that owns the file/directory the symlink is @@ -420,7 +420,7 @@ def get_gid(path, follow_symlinks=True): salt '*' file.get_gid c:\\temp\\test.txt """ - func_name = "{}.get_gid".format(__virtualname__) + func_name = f"{__virtualname__}.get_gid" if __opts__.get("fun", "") == func_name: log.info( "The function %s should not be used on Windows systems; " @@ -467,7 +467,7 @@ def get_group(path, follow_symlinks=True): salt '*' file.get_group c:\\temp\\test.txt """ - func_name = "{}.get_group".format(__virtualname__) + func_name = f"{__virtualname__}.get_group" if __opts__.get("fun", "") == func_name: log.info( "The function %s should not be used on Windows systems; " @@ -548,7 +548,7 @@ def get_uid(path, follow_symlinks=True): salt '*' file.get_uid c:\\temp\\test.txt follow_symlinks=False """ if not os.path.exists(path): - raise CommandExecutionError("Path not found: {}".format(path)) + raise CommandExecutionError(f"Path not found: {path}") # Under Windows, if the path is a symlink, the user that owns the symlink is # returned, not the user that owns the file/directory the symlink is @@ -587,7 +587,7 @@ def get_user(path, follow_symlinks=True): salt '*' file.get_user c:\\temp\\test.txt follow_symlinks=False """ if not os.path.exists(path): - raise CommandExecutionError("Path not found: {}".format(path)) + raise CommandExecutionError(f"Path not found: {path}") # Under Windows, if the path is a symlink, the user that owns the symlink is # returned, not the user that owns the file/directory the symlink is @@ -620,9 +620,9 @@ def get_mode(path): salt '*' file.get_mode /etc/passwd """ if not os.path.exists(path): - raise CommandExecutionError("Path not found: {}".format(path)) + raise CommandExecutionError(f"Path not found: {path}") - func_name = "{}.get_mode".format(__virtualname__) + func_name = f"{__virtualname__}.get_mode" if __opts__.get("fun", "") == func_name: log.info( "The function %s should not be used on Windows systems; " @@ -671,7 +671,7 @@ def lchown(path, user, group=None, pgroup=None): salt '*' file.lchown c:\\temp\\test.txt myusername "pgroup='None'" """ if group: - func_name = "{}.lchown".format(__virtualname__) + func_name = f"{__virtualname__}.lchown" if __opts__.get("fun", "") == func_name: log.info( "The group parameter has no effect when using %s on " @@ -720,7 +720,7 @@ def chown(path, user, group=None, pgroup=None, follow_symlinks=True): """ # the group parameter is not used; only provided for API compatibility if group is not None: - func_name = "{}.chown".format(__virtualname__) + func_name = f"{__virtualname__}.chown" if __opts__.get("fun", "") == func_name: log.info( "The group parameter has no effect when using %s on " @@ -733,7 +733,7 @@ def chown(path, user, group=None, pgroup=None, follow_symlinks=True): path = _resolve_symlink(path) if not os.path.exists(path): - raise CommandExecutionError("Path not found: {}".format(path)) + raise CommandExecutionError(f"Path not found: {path}") __utils__["dacl.set_owner"](path, user) if pgroup: @@ -804,7 +804,7 @@ def chgrp(path, group): salt '*' file.chpgrp c:\\temp\\test.txt administrators """ - func_name = "{}.chgrp".format(__virtualname__) + func_name = f"{__virtualname__}.chgrp" if __opts__.get("fun", "") == func_name: log.info( "The function %s should not be used on Windows systems; see " @@ -850,7 +850,7 @@ def stats(path, hash_type="sha256", follow_symlinks=True): # This is to mirror the behavior of file.py. `check_file_meta` expects an # empty dictionary when the file does not exist if not os.path.exists(path): - raise CommandExecutionError("Path not found: {}".format(path)) + raise CommandExecutionError(f"Path not found: {path}") if follow_symlinks and sys.getwindowsversion().major >= 6: path = _resolve_symlink(path) @@ -953,13 +953,13 @@ def _get_version_type(file_type, file_subtype): if ret_type == "Driver": if file_subtype in driver_subtypes: - ret_type = "{} Driver".format(driver_subtypes[file_subtype]) + ret_type = f"{driver_subtypes[file_subtype]} Driver" if ret_type == "Font": if file_subtype in font_subtypes: - ret_type = "{} Font".format(font_subtypes[file_subtype]) + ret_type = f"{font_subtypes[file_subtype]} Font" if ret_type == "Virtual Device": # The Virtual Device Identifier - ret_type = "Virtual Device: {}".format(file_subtype) + ret_type = f"Virtual Device: {file_subtype}" return ret_type @@ -1028,9 +1028,9 @@ def version(path): """ # Input validation if not os.path.exists(path): - raise CommandExecutionError("File not found: {}".format(path)) + raise CommandExecutionError(f"File not found: {path}") if os.path.isdir(path): - raise CommandExecutionError("Not a file: {}".format(path)) + raise CommandExecutionError(f"Not a file: {path}") return _get_version(path) @@ -1068,9 +1068,9 @@ def version_details(path): """ # Input validation if not os.path.exists(path): - raise CommandExecutionError("File not found: {}".format(path)) + raise CommandExecutionError(f"File not found: {path}") if os.path.isdir(path): - raise CommandExecutionError("Not a file: {}".format(path)) + raise CommandExecutionError(f"Not a file: {path}") ret = {} try: @@ -1146,7 +1146,7 @@ def get_attributes(path): salt '*' file.get_attributes c:\\temp\\a.txt """ if not os.path.exists(path): - raise CommandExecutionError("Path not found: {}".format(path)) + raise CommandExecutionError(f"Path not found: {path}") # set up dictionary for attribute values attributes = {} @@ -1228,7 +1228,7 @@ def set_attributes( salt '*' file.set_attributes c:\\temp\\a.txt readonly=True hidden=True """ if not os.path.exists(path): - raise CommandExecutionError("Path not found: {}".format(path)) + raise CommandExecutionError(f"Path not found: {path}") if normal: if archive or hidden or notIndexed or readonly or system or temporary: @@ -1297,7 +1297,7 @@ def set_mode(path, mode): salt '*' file.set_mode /etc/passwd 0644 """ - func_name = "{}.set_mode".format(__virtualname__) + func_name = f"{__virtualname__}.set_mode" if __opts__.get("fun", "") == func_name: log.info( "The function %s should not be used on Windows systems; " @@ -1333,11 +1333,11 @@ def remove(path, force=False): path = os.path.expanduser(path) if not os.path.isabs(path): - raise SaltInvocationError("File path must be absolute: {}".format(path)) + raise SaltInvocationError(f"File path must be absolute: {path}") # Does the file/folder exists if not os.path.exists(path) and not is_link(path): - raise CommandExecutionError("Path not found: {}".format(path)) + raise CommandExecutionError(f"Path not found: {path}") # Remove ReadOnly Attribute if force: @@ -1354,7 +1354,7 @@ def remove(path, force=False): os.rmdir(path) else: for name in os.listdir(path): - item = "{}\\{}".format(path, name) + item = f"{path}\\{name}" # If its a normal directory, recurse to remove it's contents remove(item, force) @@ -1364,12 +1364,12 @@ def remove(path, force=False): if force: # Reset attributes to the original if delete fails. win32api.SetFileAttributes(path, file_attributes) - raise CommandExecutionError("Could not remove '{}': {}".format(path, exc)) + raise CommandExecutionError(f"Could not remove '{path}': {exc}") return True -def symlink(src, link, force=False, atomic=False): +def symlink(src, link, force=False, atomic=False, follow_symlinks=True): """ Create a symbolic link to a file @@ -1394,6 +1394,11 @@ def symlink(src, link, force=False, atomic=False): Use atomic file operations to create the symlink .. versionadded:: 3006.0 + follow_symlinks (bool): + If set to ``False``, use ``os.path.lexists()`` for existence checks + instead of ``os.path.exists()``. + .. versionadded:: 3007.0 + Returns: bool: ``True`` if successful, otherwise raises ``CommandExecutionError`` @@ -1412,7 +1417,12 @@ def symlink(src, link, force=False, atomic=False): ) if not os.path.isabs(link): - raise SaltInvocationError("Link path must be absolute: {}".format(link)) + raise SaltInvocationError(f"Link path must be absolute: {link}") + + if follow_symlinks: + exists = os.path.exists + else: + exists = os.path.lexists if os.path.islink(link): try: @@ -1425,11 +1435,11 @@ def symlink(src, link, force=False, atomic=False): pass if not force and not atomic: - msg = "Found existing symlink: {}".format(link) + msg = f"Found existing symlink: {link}" raise CommandExecutionError(msg) - if os.path.exists(link) and not force and not atomic: - msg = "Existing path is not a symlink: {}".format(link) + if exists(link) and not force and not atomic: + msg = f"Existing path is not a symlink: {link}" raise CommandExecutionError(msg) # ensure paths are using the right slashes @@ -1443,7 +1453,7 @@ def symlink(src, link, force=False, atomic=False): th = win32security.OpenProcessToken(win32api.GetCurrentProcess(), desired_access) salt.platform.win.elevate_token(th) - if (os.path.islink(link) or os.path.exists(link)) and force and not atomic: + if (os.path.islink(link) or exists(link)) and force and not atomic: os.unlink(link) elif atomic: link_dir = os.path.dirname(link) @@ -1464,14 +1474,14 @@ def symlink(src, link, force=False, atomic=False): return True except win32file.error: os.remove(temp_link) - raise CommandExecutionError("Could not create '{}'".format(link)) + raise CommandExecutionError(f"Could not create '{link}'") try: win32file.CreateSymbolicLink(link, src, int(is_dir)) return True except win32file.error as exc: raise CommandExecutionError( - "Could not create '{}' - [{}] {}".format(link, exc.winerror, exc.strerror) + f"Could not create '{link}' - [{exc.winerror}] {exc.strerror}" ) @@ -1581,7 +1591,7 @@ def mkdir( # Make sure the drive is valid drive = os.path.splitdrive(path)[0] if not os.path.isdir(drive): - raise CommandExecutionError("Drive {} is not mapped".format(drive)) + raise CommandExecutionError(f"Drive {drive} is not mapped") path = os.path.expanduser(path) path = os.path.expandvars(path) @@ -1698,12 +1708,12 @@ def makedirs_( if os.path.isdir(dirname): # There's nothing for us to do - msg = "Directory '{}' already exists".format(dirname) + msg = f"Directory '{dirname}' already exists" log.debug(msg) return msg if os.path.exists(dirname): - msg = "The path '{}' already exists and is not a directory".format(dirname) + msg = f"The path '{dirname}' already exists and is not a directory" log.debug(msg) return msg @@ -1912,7 +1922,7 @@ def check_perms( salt '*' file.check_perms C:\\Temp\\ {} Administrators "{'jsnuffy': {'perms': ['read_attributes', 'read_ea'], 'applies_to': 'files_only'}}" """ if not os.path.exists(path): - raise CommandExecutionError("Path not found: {}".format(path)) + raise CommandExecutionError(f"Path not found: {path}") path = os.path.expanduser(path)