Hallo, dies ist ein Test.
PWD: /www/data-lst1/unixsoft/unixsoft/kaempfer/.public_html
Running in File Mode
Relative path: ./../../../../../../lib/svc/share/./migrate_shared_files.py
Real path: /lib/svc/share/migrate_shared_files.py
Zurück
#!/usr/bin/python3.7 -Es import solaris.no_site_packages # # Copyright (c) 2012, 2022, Oracle and/or its affiliates. # # This program relocates any files (and copies any directories) found # under the first argument to the second argument, using the third # argument as a tag as part of the user messages explaining what is # happening. # # If a file by the same path already exists, the event is logged to stderr # but the command still exits with a zero exit code. Such files have # ".NOT-MIGRATED-EXISTS-" prepended to their name. The following will cause # the command to exit with a non-zero code: # # If a file is not a directory, symlink or a regular file, it is # not migrated and instead it is renamed to ".NOT-MIGRATED-UNSUPPORTED-". # # Attempts to change an objects type (file > dir, dir > file) # # Only files, directories and symlinks are moved, attempts to move different # filetypes are logged, but will not cause a non-zero exit code. # All actions are logged to stderr. Overall progress is logged to stdout. import curses import os from stat import * import errno import sys import ctypes import time # definition of reflink() + ___errno() libc = ctypes.CDLL("libc.so.1") # int reflink(char *path1, const char *path2, int preserve) libc.reflink.argtypes = [ctypes.c_char_p, ctypes.c_char_p, ctypes.c_int] libc.reflink.restype = ctypes.c_int libc.___errno.argtypes = [] libc.___errno.restype = ctypes.POINTER(ctypes.c_int) def errcheck(ret, func, args): if ret == -1: e = libc.___errno()[0] raise OSError(e, os.strerror(e)) return ret def reflink(source, dest, preserve): return libc.reflink(bytes(source, 'utf-8'), bytes(dest, 'utf-8'), preserve) reflink.errcheck = errcheck def _meg(number): return int(number / 1024 / 1024) class MigrationProgress(object): """A class to keep the user informed of our progress while migrating directory contents. We only output progress information if we're moving more than a certain amount of data.""" def __init__(self, source, dest, svc): """Initialise our progress meter, with 'source' as the directory we intend to migrate, 'dest' as the location to migrate to, and 'svc' as the name of the service performing the migration.""" self.sizes = {} self.total = 0 self.completed = 0 self.print_progress = False # How many megabytes of data we must be moving before # choosing to output progress self.threshold = 200 self.clear_eol = "" self.cr = "\n" self.banner = "%(svc)s moving data to %(dest)s:" % locals() total = 0 for dirpath, dirnames, filenames in os.walk(source): for name in filenames: path = os.path.join(dirpath, name) size = 0 if os.path.isfile(path) and not os.path.islink(path): size = os.path.getsize(path) self.sizes[path] = size self.total += size # if we're migrating a lot of data, merely # working out how much data is involved can # take some time - once we go over a certain # threshold, we tell the user what we're doing. if not self.print_progress and \ _meg(self.total) > self.threshold: os.environ.setdefault("TERM", "sun-color") self.setup_term() print(self.banner, end=' ') sys.stdout.flush() self.print_progress = True def setup_term(self): if not os.isatty(sys.stdout.fileno()): return curses.setupterm() self.clear_eol = curses.tigetstr("el") self.cr = curses.tigetstr("cr") if self.clear_eol: self.clear_eol = str(self.clear_eol, 'utf-8') if not self.clear_eol: self.clear_eol = "" if self.cr: self.cr = str(self.cr, 'utf-8') if not self.cr: self.cr = "\n" def update(self, file_name): """update our progress meter.""" if not self.print_progress: return print(self.cr, end=' ') last_prog = _meg(self.completed) self.completed += self.sizes[file_name] cur_prog = _meg(self.completed) if cur_prog > last_prog: s = (self.banner + " %d/%d MB" % (_meg(self.completed), _meg(self.total))) sys.stdout.write(s + self.clear_eol) sys.stdout.flush() def done(self): s = self.cr + self.banner + " Complete." sys.stdout.write(s + self.clear_eol + "\n") sys.stdout.flush() progress = None # the top level dir being migrated, to use when resolving symlinks source_root = None def main_func(): global progress global source_root if len(sys.argv) != 4: logit("Error: must be called with source, dest and FMRI arguments") return 2 src, dest, fmri = sys.argv[1:4] source_root = os.path.abspath(src) src = source_root dest = os.path.abspath(dest) for path in src, dest: no_dir = False if not os.path.isdir(path): no_dir = True logit("Error: %s is not a directory" % path) if no_dir: return 1 progress = MigrationProgress(src, dest, fmri) ret = migrate_dir(src, dest) if progress.print_progress: progress.done() if ret > 0: logit("Error: Unable to move some contents of %s to %s." % (src, dest)) return 1 return 0 def logit(string): """Show the string on the stderr""" print("%s: %s" % (sys.argv[0], string), file=sys.stderr) def migrate_symlink(source, dest): """Tries to migrate a symlink from source to dest_path, returns False if the source is not a symlink, or there were problems trying to move the file. We assume that source exists. If the destination exists and is a symlink, we rename the source and return. """ try: target = os.readlink(source) reltgt = target try: d_st = os.lstat(dest) except OSError as e: d_st = None # an existing symlink at the destination causes # the source to be renamed and we return immediately if d_st and S_ISLNK(d_st.st_mode): source_dir = os.path.dirname(source) source_base = os.path.basename(source) new_name = os.path.join(source_dir, ".NOT-MIGRATED-EXISTS-%s" % source_base) os.rename(source, new_name) logit("Info: %s exists already; moved to %s" % (dest, new_name)) return True # we can deal with absolute links, or links whose targets are # going to be moved too, so we'll try to resolve the target if not os.path.isabs(target): reltgt = os.path.join(os.path.dirname(source), target) reltgt = os.path.normpath(reltgt) if reltgt.startswith(source_root) or os.path.isabs(target): os.symlink(target, dest) os.unlink(source) logit("Info: migrated link: %s" % (dest)) return True # we've got a relative link that's linked to somewhere above # the source_root. This is impossible to fix, since we don't # know where this link was migrated from, and so don't know # what it should be relative to. logit("Error: Unable to move relative link from %s to %s." % (source, dest)) return False except Exception as e: logit("Error: Problem moving symlink for %s: %s" % (source, e)) return False def migrate_dir(source_path, dest_path): """ handle migration""" exit_code = 0 for f in os.listdir(source_path): if f.startswith(".NOT-MIGRATED-"): continue source = os.path.join(source_path, f) dest = os.path.join(dest_path, f) try: s_st = os.lstat(source) except OSError as e: if e.errno == errno.ENOENT: logit("Error: unable to migrate %s: " "no such file or directory" % source) else: logit("Error: cannot migrate %s unable to stat" "existing file at %s: %s" % (source, dest, e)) exit_code += 1 continue # Deal with possible symlinks first. if S_ISLNK(s_st.st_mode): if not migrate_symlink(source, dest): exit_code += 1 continue else: s_st = os.stat(source) if S_ISDIR(s_st.st_mode): try: d_st = os.stat(dest) # ensure mode/ownership changes are preserved if d_st.st_uid != s_st.st_uid or d_st.st_gid != s_st.st_gid: os.chown(dest, s_st.st_uid, s_st.st_gid) logit("Info: changed ownership on %s" % dest) if d_st.st_mode != s_st.st_mode: os.chmod(dest, s_st.st_mode) logit("Info: changed mode on %s" % dest) except OSError as e: # if the dest doesn't exist, create it if e.errno == errno.ENOENT: os.mkdir(dest) os.chown(dest, s_st.st_uid, s_st.st_gid) os.chmod(dest, s_st.st_mode) logit("Info: migrated directory %s" % dest) exit_code += migrate_dir(source, dest) continue # otherwise, something else is wrong logit("Error: unable to migrate %s: %s" % (source, e)) exit_code += 1 continue if S_ISDIR(d_st.st_mode): exit_code += migrate_dir(source, dest) else: # destination exists and is not a directory logit("Error: %s is dir; %s is not" % (source, dest)) return 1 elif S_ISREG(s_st.st_mode): try: d_st = os.stat(dest) except OSError as e: if e.errno == errno.ENOENT: # time to copy the file exit_code += migrate_file(source, dest, s_st) continue else: logit("Error: cannot migrate %s unable " "to stat existing file at %s: %s" % (source, dest, e)) exit_code += 1 continue # file already exists; rename file so we ignore from now # on. try: d_st = os.stat(dest) except Exception as e: logit("Error: cannot migrate %s unable to stat" "existing file at %s: %s" % (source, dest, e)) exit_code += 1 continue if S_ISREG(d_st.st_mode): new_name = os.path.join(source_path, ".NOT-MIGRATED-EXISTS-%s" % f) os.rename(source, new_name) logit("Info: %s exists already; moved to %s" % (dest, new_name)) else: exit_code += 1 logit("Error: %s is file; %s is not" % (source, dest)) elif S_ISSOCK(s_st.st_mode) or S_ISFIFO(s_st.st_mode): os.unlink(source) logit("Warning: %s is a socket or pipe; " "it is removed and not migrated" % (source)) else: new_name = os.path.join(source_path, ".NOT-MIGRATED-UNSUPPORTED-%s" % f) os.rename(source, new_name) logit("Warning: %s is not file, directory or symlink; " "moved to %s" % (source, new_name)) return exit_code def migrate_file(source, dest, s_st): exit_code = 0 t1 = time.time() copywith = "reflink" try: # Do not use reflink for small files; it takes too long. if s_st.st_size < 100000: raise OSError(0, "No Error") reflink(source, dest, 1) os.chown(dest, s_st.st_uid, s_st.st_gid) os.utime(dest, (s_st.st_atime, s_st.st_mtime)) os.chmod(dest, s_st.st_mode) os.unlink(source) except OSError as e: copywith = "cp" # reflink() failed, copy the file. # time to copy the file try: with open(source, "rb") as s, open(dest, "wb") as d: while True: # 128k is the default ZFS record size buff = s.read(128 * 1024) if not buff: break d.write(buff) os.chown(dest, s_st.st_uid, s_st.st_gid) os.utime(dest, (s_st.st_atime, s_st.st_mtime)) os.chmod(dest, s_st.st_mode) os.unlink(source) except Exception as e: logit("Error: cannot migrate file %s:%s" % (source, e)) exit_code += 1 return exit_code finally: t2 = time.time() logit("Info: migrated file: %s (%s, %fs)" % (dest, copywith, t2 - t1)) progress.update(source) return exit_code if __name__ == "__main__": sys.exit(main_func())