mirror of https://github.com/trapexit/mergerfs.git
Antonio SJ Musumeci
9 years ago
3 changed files with 4 additions and 132 deletions
-
7Makefile
-
4README.md
-
125tools/fsck.mergerfs
@ -1,125 +0,0 @@ |
|||||
#!/usr/bin/python |
|
||||
|
|
||||
# Copyright (c) 2016, Antonio SJ Musumeci <trapexit@spawn.link> |
|
||||
|
|
||||
# Permission to use, copy, modify, and/or distribute this software for any |
|
||||
# purpose with or without fee is hereby granted, provided that the above |
|
||||
# copyright notice and this permission notice appear in all copies. |
|
||||
|
|
||||
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|
||||
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|
||||
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|
||||
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|
||||
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|
||||
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|
||||
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|
||||
|
|
||||
import argparse |
|
||||
import os |
|
||||
import xattr |
|
||||
import errno |
|
||||
|
|
||||
|
|
||||
def main(): |
|
||||
parser = argparse.ArgumentParser(description='audit a mergerfs mount for inconsistencies') |
|
||||
parser.add_argument('device',type=str,help='device') |
|
||||
parser.add_argument('-v','--verbose',action='store_true',help='print details of audit item') |
|
||||
parser.add_argument('-f','--fix',choices=['manual','newest'],help='fix policy') |
|
||||
|
|
||||
args = parser.parse_args() |
|
||||
|
|
||||
if args.fix: |
|
||||
args.verbose = True |
|
||||
|
|
||||
if args.fix == 'manual': |
|
||||
fix = manual_fix |
|
||||
elif args.fix == 'newest': |
|
||||
fix = newest_fix |
|
||||
else: |
|
||||
fix = noop_fix |
|
||||
|
|
||||
try: |
|
||||
controlfile = os.path.join(args.device,".mergerfs") |
|
||||
version = xattr.getxattr(controlfile,"user.mergerfs.version") |
|
||||
|
|
||||
for (dirname,dirnames,filenames) in os.walk(args.device): |
|
||||
fulldirpath = os.path.join(args.device,dirname) |
|
||||
check_consistancy(fulldirpath,args.verbose,fix) |
|
||||
for filename in filenames: |
|
||||
fullpath = os.path.join(fulldirpath,filename) |
|
||||
check_consistancy(fullpath,args.verbose,fix) |
|
||||
|
|
||||
except IOError as e: |
|
||||
if e.errno == errno.ENOENT: |
|
||||
print("%s is not a mergerfs device" % args.device) |
|
||||
else: |
|
||||
print("IOError: %s" % e.strerror) |
|
||||
|
|
||||
|
|
||||
def check_consistancy(fullpath,verbose,fix): |
|
||||
paths = xattr.getxattr(fullpath,"user.mergerfs.allpaths").split('\0') |
|
||||
if len(paths) > 1: |
|
||||
stats = [os.stat(path) for path in paths] |
|
||||
if stats_different(stats): |
|
||||
print("mismatch: %s" % fullpath) |
|
||||
if verbose: |
|
||||
print_stats(paths,stats) |
|
||||
fix(paths,stats) |
|
||||
|
|
||||
|
|
||||
def noop_fix(paths,stats): |
|
||||
pass |
|
||||
|
|
||||
|
|
||||
def manual_fix(paths,stats): |
|
||||
done = False |
|
||||
while not done: |
|
||||
try: |
|
||||
value = input('Which is correct?: ') |
|
||||
setstat(stats[value % len(paths)],paths) |
|
||||
done = True |
|
||||
except NameError: |
|
||||
print("Input error: enter a value between 0 and %d" % (len(paths)-1)) |
|
||||
except Exception as e: |
|
||||
print("%s" % e) |
|
||||
done = True |
|
||||
|
|
||||
|
|
||||
def newest_fix(paths,stats): |
|
||||
stats.sort(key=lambda stat: stat.st_mtime) |
|
||||
try: |
|
||||
setstat(stats[-1],paths) |
|
||||
except Exception as e: |
|
||||
print("%s" % e) |
|
||||
|
|
||||
|
|
||||
def setstat(stat,paths): |
|
||||
for path in paths: |
|
||||
try: |
|
||||
os.chmod(path,stat.st_mode) |
|
||||
os.chown(path,stat.st_uid,stat.st_gid); |
|
||||
print("setting %s > uid: %d gid: %d mode: %o" % |
|
||||
(path,stat.st_uid,stat.st_gid,stat.st_mode)) |
|
||||
except Exception as e: |
|
||||
print("%s" % e) |
|
||||
|
|
||||
|
|
||||
def stats_different(stats): |
|
||||
base = stats[0] |
|
||||
for stat in stats: |
|
||||
if ((stat.st_mode == base.st_mode) and |
|
||||
(stat.st_uid == base.st_uid) and |
|
||||
(stat.st_gid == base.st_gid)): |
|
||||
continue |
|
||||
return True |
|
||||
return False |
|
||||
|
|
||||
|
|
||||
def print_stats(Files,Stats): |
|
||||
for i in xrange(0,len(Files)): |
|
||||
print("- %i: %s > uid: %s; gid: %s; mode: %o" % |
|
||||
(i,Files[i],Stats[i].st_uid,Stats[i].st_gid,Stats[i].st_mode)) |
|
||||
|
|
||||
|
|
||||
if __name__ == "__main__": |
|
||||
main() |
|
Write
Preview
Loading…
Cancel
Save
Reference in new issue