Hi Miklos, Attached is the beginnings of a tool that produces an HTML graphical schematic of the errors returned by various path-taking syscalls (eg. open) for all the relevant flags (leastways, the ones Python3 can operate). To run it: ./error-probe.py >error.html konqueror ./error.html # ... or whatever browser you prefer It does its probing in /tmp/a/ but that is easily configurable by editing the script. I intend to break the tool into two parts: (1) A tool that probes a directory and produces a report file. (2) A tool that produces a graphical schematic comparing two report files. David --- #!/usr/bin/python3 # # Probe for error messages # base = "/tmp/a/" test = base + "foo" test2 = base + "bar" import os, errno, shutil def prep_neg(): """N""" return test def prep_file(): """F""" fd = os.open(test, os.O_CREAT|os.O_WRONLY) os.write(fd, b"hello") os.close(fd) return test def prep_empty_dir(): """E""" os.mkdir(test) return test def prep_dir(): """D""" os.mkdir(test) fd = os.open(test + "/a", os.O_CREAT|os.O_WRONLY) os.write(fd, b"hello") os.close(fd) return test def prep_sym_neg(): """LN""" os.symlink(test2, test) return test def prep_sym_file(): """LF""" fd = os.open(test2, os.O_CREAT|os.O_WRONLY) os.write(fd, b"hello") os.close(fd) os.symlink(test2, test) return test def prep_sym_empty_dir(): """LE""" os.mkdir(test2) os.symlink(test2, test) return test def prep_sym_dir(): """LD""" os.mkdir(test2) fd = os.open(test2 + "/a", os.O_CREAT|os.O_WRONLY) os.write(fd, b"hello") os.close(fd) os.symlink(test2, test) return test def prep_neg_com(): """CN""" name = prep_neg() return name + "/a" def prep_file_com(): """CF""" name = prep_file() return name + "/a" def prep_symneg_com(): """CLN""" name = prep_sym_neg() return name + "/a" def prep_symfile_com(): """CLF""" name = prep_sym_file() return name + "/a" results_table = [] prep_funcs = [ prep_neg, prep_file, prep_empty_dir, prep_dir, prep_sym_neg, prep_sym_file, prep_sym_empty_dir, prep_sym_dir, prep_neg_com, prep_file_com, prep_symneg_com, prep_symfile_com ] banner = [ "Syscall", "Flags" ] for ts in [ "", "/" ]: for prep in prep_funcs: banner.append(prep.__doc__ + ts) results = [] file_nr = 0 def begin(name, flags="-"): global file_nr, results results = [ (name, False), (flags, False) ] shutil.rmtree(base) os.mkdir(base) file_nr = 0 def advance(): global file_nr, test, test2 file_nr += 1 test = base + "foo" + str(file_nr) test2 = base + "bar" + str(file_nr) def no_error(): global results results.append(("+", False)) def got_error(oe): global results err = errno.errorcode[oe.errno] results.append((err, False)) def commit(): global results_table, results, test, test2 results_table.append(results) results = None test = None test2 = None ############################################################################### # # Test open # ############################################################################### for d in [ 0, os.O_DIRECTORY ]: for c in [ 0, os.O_CREAT ]: for e in [ 0, os.O_EXCL ]: for t in [ 0, os.O_TRUNC ]: for a in [ 0, os.O_APPEND ]: for rw in [ os.O_RDONLY, os.O_WRONLY, os.O_RDWR ]: flags = "" if d == os.O_DIRECTORY: flags += "d" if c == os.O_CREAT: flags += "c" if e == os.O_EXCL: flags += "e" if t == os.O_TRUNC: flags += "t" if a == os.O_APPEND: flags += "a" if rw == os.O_RDONLY or rw == os.O_RDWR: flags += "r" if rw == os.O_WRONLY or rw == os.O_RDWR: flags += "w" begin("open", flags) for ts in [ "", "/" ]: for prep in prep_funcs: advance() f = prep() try: fd = os.open(f + ts, rw|a|c|e|t|d) os.close(fd) no_error() except OSError as oe: got_error(oe) commit() ############################################################################### # # Test unlink # ############################################################################### begin("unlink") for ts in [ "", "/" ]: for prep in prep_funcs: advance() f = prep() try: os.unlink(f + ts) no_error() except OSError as oe: got_error(oe) commit() ############################################################################### # # Test mkfifo # ############################################################################### begin("mkfifo") for ts in [ "", "/" ]: for prep in prep_funcs: advance() f = prep() try: os.mkfifo(f + ts) no_error() except OSError as oe: got_error(oe) commit() ############################################################################### # # Test symlink # ############################################################################### begin("symlink") for ts in [ "", "/" ]: for prep in prep_funcs: advance() f = prep() try: os.symlink(test + "_sym", f + ts) no_error() except OSError as oe: got_error(oe) commit() ############################################################################### # # Test mkdir # ############################################################################### begin("mkdir") for ts in [ "", "/" ]: for prep in prep_funcs: advance() f = prep() try: os.mkdir(f + ts) no_error() except OSError as oe: got_error(oe) commit() ############################################################################### # # Test chmod # ############################################################################### begin("chmod") for ts in [ "", "/" ]: for prep in prep_funcs: advance() f = prep() try: os.chmod(f + ts, 0o755) no_error() except OSError as oe: got_error(oe) commit() ############################################################################### # # Test chown # ############################################################################### begin("chown") for ts in [ "", "/" ]: for prep in prep_funcs: advance() f = prep() try: os.chown(f + ts, os.getuid(), os.getgid()) no_error() except OSError as oe: got_error(oe) commit() ############################################################################### # # Test rmdir # ############################################################################### begin("rmdir") for ts in [ "", "/" ]: for prep in prep_funcs: advance() f = prep() try: os.rmdir(f + ts) no_error() except OSError as oe: got_error(oe) commit() ############################################################################### # # Test truncate # ############################################################################### begin("truncate") for ts in [ "", "/" ]: for prep in prep_funcs: advance() f = prep() try: os.truncate(f + ts, 1) no_error() except OSError as oe: got_error(oe) commit() ############################################################################### # # Test readlink # ############################################################################### begin("readlink") for ts in [ "", "/" ]: for prep in prep_funcs: advance() f = prep() try: os.readlink(f + ts) no_error() except OSError as oe: got_error(oe) commit() ############################################################################### # # Test access # ############################################################################### for flag in [ "-", "n" ]: begin("access", flag) for ts in [ "", "/" ]: for prep in prep_funcs: advance() f = prep() try: if flag == "n": os.access(f + ts, os.R_OK, follow_symlinks=False) else: os.access(f + ts, os.R_OK) no_error() except OSError as oe: got_error(oe) commit() ############################################################################### # # Test stat # ############################################################################### for flag in [ "-", "n" ]: begin("stat", flag) for ts in [ "", "/" ]: for prep in prep_funcs: advance() f = prep() try: if flag == "n": os.stat(f + ts, follow_symlinks=False) else: os.stat(f + ts) no_error() except OSError as oe: got_error(oe) commit() ############################################################################### # # Test utime # ############################################################################### for flag in [ "-", "n" ]: begin("utime", flag) for ts in [ "", "/" ]: for prep in prep_funcs: advance() f = prep() try: if flag == "n": os.utime(f + ts, follow_symlinks=False) else: os.utime(f + ts) no_error() except OSError as oe: got_error(oe) commit() ############################################################################### # # Test getxattr # ############################################################################### for flag in [ "-", "n" ]: begin("getxattr", flag) for ts in [ "", "/" ]: for prep in prep_funcs: advance() f = prep() try: if flag == "n": os.getxattr(f + ts, "security.selinux", follow_symlinks=False) else: os.getxattr(f + ts, "security.selinux") no_error() except OSError as oe: got_error(oe) commit() ############################################################################### # # Test listxattr # ############################################################################### for flag in [ "-", "n" ]: begin("listxattr", flag) for ts in [ "", "/" ]: for prep in prep_funcs: advance() f = prep() try: if flag == "n": os.listxattr(f + ts, follow_symlinks=False) else: os.listxattr(f + ts) no_error() except OSError as oe: got_error(oe) commit() ############################################################################### # # Test rename # ############################################################################### for ts_s in [ "", "/" ]: for prep_s in prep_funcs: slash = "" if ts_s == "/": slash = "/" begin("rename", "|<CODE><B>" + prep_s.__doc__ + slash + "</B></CODE>") for ts_d in [ "", "/" ]: for prep_d in prep_funcs: advance() f_s = prep_s() advance() f_d = prep_d() try: os.rename(f_s + ts_s, f_d + ts_d) no_error() except OSError as oe: got_error(oe) commit() ############################################################################### # # Test hard linking # ############################################################################### for ts_s in [ "", "/" ]: for prep_s in prep_funcs: slash = "" if ts_s == "/": slash = "/" begin("link", "|<CODE><B>" + prep_s.__doc__ + slash + "</B></CODE>") for ts_d in [ "", "/" ]: for prep_d in prep_funcs: advance() f_s = prep_s() advance() f_d = prep_d() try: os.link(f_s + ts_s, f_d + ts_d) no_error() except OSError as oe: got_error(oe) commit() ############################################################################### # # Dump the results # ############################################################################### #for r in results_table: # print(r[0:2]) #exit(88) nrow = len(results_table) ncol = len(results_table[0]) colours = { "+" : "lightgreen", "ENOENT" : "#fff0f0", "ENOTDIR" : "#f0c080", "EISDIR" : "#ffffb0", "EEXIST" : "#f04040", "ENOTEMPTY" : "#b04040", "EINVAL" : "#5040ff", "EPERM" : "#7030ff", } print("<HTML>") print("<BODY>") print("<TABLE border=1>") print("<TR><TD><CODE><B>N</B></CODE></TD><TD>Non-existent name</TD></TR>") print("<TR><TD><CODE><B>F</B></CODE></TD><TD>Ordinary file</TD></TR>") print("<TR><TD><CODE><B>E</B></CODE></TD><TD>Empty directory</TD></TR>") print("<TR><TD><CODE><B>D</B></CODE></TD><TD>Populated directory</TD></TR>") print("<TR><TD><CODE><B>L<I>x</I></B></CODE></TD><TD>Symlink to <I>x</I></TD></TR>") print("<TR><TD><CODE><B>C<I>x</I></B></CODE></TD><TD>Non-directory path component <I>x</I></TD></TR>") print("<TR><TD><CODE><B>/</B></CODE></TD><TD>Pathname terminated with a '/'</TD></TR>") print("</TABLE>") print("<P>") print("<TABLE rules=\"rows\">") print("<COLGROUP>") print("<COL width=\"1*\">") print("<COL width=\"1*\">") print("</COLGROUP>") print("<COLGROUP>") for c in range(2, ncol): print("<COL width=\"1*\">") print("</COLGROUP>") print("<TR>") print("<TH>" + banner[0] + "</TH>") print("<TH>" + banner[1] + "</TH>") for val in banner[2:]: #if len(val) < 7: # val += " "[0:(7 - len(val))] for i in range(0, 7 - len(val)): val += " " print("<TH><CODE>" + val + "</CODE></TH>") print("</TR>") def extract_key(a): return -a[0] # Merge together rectangular regions with same values in order of # descending size rects = [] for r in range(0, nrow): row = results_table[r] for c in range(0, ncol): if c == 1: continue hspan = 1 vspan = 1 (val, merged) = results_table[r][c] for nc in range(c + 1, ncol): if nc == 1: break if row[nc] != (val, False): break hspan += 1 for nr in range(r + 1, nrow): if results_table[nr][c] != (val, False): break vspan += 1 for nc in range(c + 1, c + hspan): if results_table[nr][nc] != (val, False): hspan = nc - c break assert(r + vspan <= nrow) assert(c + hspan <= ncol) if hspan * vspan >= 2: rects.append((hspan * vspan, r, c, hspan, vspan)) rects.sort(key=extract_key) for n in range(0, len(rects)): rect = rects[n] if rect == None: continue (area, r, c, hspan, vspan) = rect # Eliminate overlapping rectangles for n2 in range(n, len(rects)): rect2 = rects[n2] if rect2 == None: continue (area2, r2, c2, hspan2, vspan2) = rect2 if r2 > r + vspan - 1 or r2 + vspan2 - 1 < r: continue if c2 > c + hspan - 1 or c2 + hspan2 - 1 < c: continue # We could shrink the smaller rectangle, but that would # necessitate moving it within rects[] rects[n2] = None # Mark merged cells (val, merged) = results_table[r][c] for nr in range(r, r + vspan): for nc in range(c, c + hspan): results_table[nr][nc] = (val, True) results_table[r][c] = (val, rect) # Dump the table for r in range(0, nrow): row = results_table[r] print("<TR>") for c in range(0, ncol): (val, merged) = results_table[r][c] if merged == True: continue hspan = 1 vspan = 1 if c == 1: pass elif merged == False: for nc in range(c + 1, ncol): if row[nc] != (val, False): break hspan += 1 for nr in range(r + 1, nrow): if results_table[nr][c] != (val, False): break vspan += 1 for nc in range(c + 1, c + hspan): if results_table[nr][nc] != (val, False): hspan = nc - c break if hspan > 1 or vspan > 1: for nr in range(r, r + vspan): for nc in range(c, c + hspan): if nr == r and nc == c: continue results_table[nr][nc] = (val, True) else: (area, r, c, hspan, vspan) = merged colour = "white" if val in colours: colour = colours[val] al = "left" if c >= 2: al = "center" print("<TD rowspan=\"" + str(vspan) + "\" colspan=\"" + str(hspan) + "\" bgcolor=\"" + colour + "\" align=\"" + al + "\">" + val + "</TD>") print("</TR>") print("</TABLE>") print("</BODY>") print("</HTML>") -- To unsubscribe from this list: send the line "unsubscribe linux-unionfs" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html