|
Packit Service |
bbaf07 |
#!/usr/bin/env python3
|
|
Packit Service |
bbaf07 |
#
|
|
Packit Service |
bbaf07 |
# Script to check for inconsistencies between documented mount options
|
|
Packit Service |
bbaf07 |
# and implemented kernel options.
|
|
Packit Service |
bbaf07 |
# Copyright (C) 2018 Aurelien Aptel (aaptel@suse.com)
|
|
Packit Service |
bbaf07 |
#
|
|
Packit Service |
bbaf07 |
# This program is free software; you can redistribute it and/or modify
|
|
Packit Service |
bbaf07 |
# it under the terms of the GNU General Public License as published by
|
|
Packit Service |
bbaf07 |
# the Free Software Foundation; either version 3 of the License, or
|
|
Packit Service |
bbaf07 |
# (at your option) any later version.
|
|
Packit Service |
bbaf07 |
#
|
|
Packit Service |
bbaf07 |
# This program is distributed in the hope that it will be useful,
|
|
Packit Service |
bbaf07 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit Service |
bbaf07 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
Packit Service |
bbaf07 |
# GNU General Public License for more details.
|
|
Packit Service |
bbaf07 |
#
|
|
Packit Service |
bbaf07 |
# You should have received a copy of the GNU General Public License
|
|
Packit Service |
bbaf07 |
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
Packit Service |
bbaf07 |
|
|
Packit Service |
bbaf07 |
import os
|
|
Packit Service |
bbaf07 |
import sys
|
|
Packit Service |
bbaf07 |
import re
|
|
Packit Service |
bbaf07 |
import subprocess
|
|
Packit Service |
bbaf07 |
import argparse
|
|
Packit Service |
bbaf07 |
from pprint import pprint as P
|
|
Packit Service |
bbaf07 |
|
|
Packit Service |
bbaf07 |
def extract_canonical_opts(s):
|
|
Packit Service |
bbaf07 |
"""
|
|
Packit Service |
bbaf07 |
Return list of option names present in s.
|
|
Packit Service |
bbaf07 |
e.g "opt1=a|opt2=d" => ["opt1", "opt2"])
|
|
Packit Service |
bbaf07 |
"""
|
|
Packit Service |
bbaf07 |
opts = s.split("|")
|
|
Packit Service |
bbaf07 |
res = []
|
|
Packit Service |
bbaf07 |
for o in opts:
|
|
Packit Service |
bbaf07 |
x = o.split("=")
|
|
Packit Service |
bbaf07 |
res.append(x[0])
|
|
Packit Service |
bbaf07 |
return res
|
|
Packit Service |
bbaf07 |
|
|
Packit Service |
bbaf07 |
def extract_kernel_opts(fn):
|
|
Packit Service |
bbaf07 |
STATE_BASE = 0
|
|
Packit Service |
bbaf07 |
STATE_DEF = 1
|
|
Packit Service |
bbaf07 |
STATE_USE = 2
|
|
Packit Service |
bbaf07 |
STATE_EXIT = 3
|
|
Packit Service |
bbaf07 |
|
|
Packit Service |
bbaf07 |
state = STATE_BASE
|
|
Packit Service |
bbaf07 |
fmt2enum = {}
|
|
Packit Service |
bbaf07 |
enum2code = {}
|
|
Packit Service |
bbaf07 |
code = ''
|
|
Packit Service |
bbaf07 |
current_opt = ''
|
|
Packit Service |
bbaf07 |
rx = RX()
|
|
Packit Service |
bbaf07 |
|
|
Packit Service |
bbaf07 |
def code_add(s):
|
|
Packit Service |
bbaf07 |
if current_opt != '':
|
|
Packit Service |
bbaf07 |
if current_opt not in enum2code:
|
|
Packit Service |
bbaf07 |
enum2code[current_opt] = ''
|
|
Packit Service |
bbaf07 |
enum2code[current_opt] += s
|
|
Packit Service |
bbaf07 |
|
|
Packit Service |
bbaf07 |
with open(fn) as f:
|
|
Packit Service |
bbaf07 |
for s in f.readlines():
|
|
Packit Service |
bbaf07 |
if state == STATE_EXIT:
|
|
Packit Service |
bbaf07 |
break
|
|
Packit Service |
bbaf07 |
|
|
Packit Service |
bbaf07 |
elif state == STATE_BASE:
|
|
Packit Service |
bbaf07 |
if rx.search(r'cifs_mount_option_tokens.*\{', s):
|
|
Packit Service |
bbaf07 |
state = STATE_DEF
|
|
Packit Service |
bbaf07 |
elif rx.search(r'^cifs_parse_mount_options', s):
|
|
Packit Service |
bbaf07 |
state = STATE_USE
|
|
Packit Service |
bbaf07 |
|
|
Packit Service |
bbaf07 |
elif state == STATE_DEF:
|
|
Packit Service |
bbaf07 |
if rx.search(r'(Opt_[a-zA-Z0-9_]+)\s*,\s*"([^"]+)"', s):
|
|
Packit Service |
bbaf07 |
fmt = rx.group(2)
|
|
Packit Service |
bbaf07 |
opts = extract_canonical_opts(fmt)
|
|
Packit Service |
bbaf07 |
assert(len(opts) == 1)
|
|
Packit Service |
bbaf07 |
name = opts[0]
|
|
Packit Service |
bbaf07 |
fmt2enum[name] = {'enum':rx.group(1), 'fmt':fmt}
|
|
Packit Service |
bbaf07 |
elif rx.search(r'^};', s):
|
|
Packit Service |
bbaf07 |
state = STATE_BASE
|
|
Packit Service |
bbaf07 |
|
|
Packit Service |
bbaf07 |
elif state == STATE_USE:
|
|
Packit Service |
bbaf07 |
if rx.search(r'^\s*case (Opt_[a-zA-Z0-9_]+)', s):
|
|
Packit Service |
bbaf07 |
current_opt = rx.group(1)
|
|
Packit Service |
bbaf07 |
elif current_opt != '' and rx.search(r'^\s*default:', s):
|
|
Packit Service |
bbaf07 |
state = STATE_EXIT
|
|
Packit Service |
bbaf07 |
else:
|
|
Packit Service |
bbaf07 |
code_add(s)
|
|
Packit Service |
bbaf07 |
return fmt2enum, enum2code
|
|
Packit Service |
bbaf07 |
|
|
Packit Service |
bbaf07 |
def chomp(s):
|
|
Packit Service |
bbaf07 |
if s[-1] == '\n':
|
|
Packit Service |
bbaf07 |
return s[:-1]
|
|
Packit Service |
bbaf07 |
return s
|
|
Packit Service |
bbaf07 |
|
|
Packit Service |
bbaf07 |
def extract_man_opts(fn):
|
|
Packit Service |
bbaf07 |
STATE_EXIT = 0
|
|
Packit Service |
bbaf07 |
STATE_BASE = 1
|
|
Packit Service |
bbaf07 |
STATE_OPT = 2
|
|
Packit Service |
bbaf07 |
|
|
Packit Service |
bbaf07 |
state = STATE_BASE
|
|
Packit Service |
bbaf07 |
rx = RX()
|
|
Packit Service |
bbaf07 |
opts = {}
|
|
Packit Service |
5e7916 |
ln = 0
|
|
Packit Service |
bbaf07 |
|
|
Packit Service |
bbaf07 |
with open(fn) as f:
|
|
Packit Service |
bbaf07 |
for s in f.readlines():
|
|
Packit Service |
5e7916 |
ln += 1
|
|
Packit Service |
5e7916 |
|
|
Packit Service |
bbaf07 |
if state == STATE_EXIT:
|
|
Packit Service |
bbaf07 |
break
|
|
Packit Service |
bbaf07 |
|
|
Packit Service |
bbaf07 |
elif state == STATE_BASE:
|
|
Packit Service |
bbaf07 |
if rx.search(r'^OPTION', s):
|
|
Packit Service |
bbaf07 |
state = STATE_OPT
|
|
Packit Service |
bbaf07 |
|
|
Packit Service |
bbaf07 |
elif state == STATE_OPT:
|
|
Packit Service |
bbaf07 |
if rx.search('^[a-z]', s) and len(s) < 50:
|
|
Packit Service |
bbaf07 |
s = chomp(s)
|
|
Packit Service |
bbaf07 |
names = extract_canonical_opts(s)
|
|
Packit Service |
bbaf07 |
for name in names:
|
|
Packit Service |
5e7916 |
if name not in opts:
|
|
Packit Service |
5e7916 |
opts[name] = []
|
|
Packit Service |
5e7916 |
opts[name].append({'ln':ln, 'fmt':s})
|
|
Packit Service |
bbaf07 |
elif rx.search(r'^[A-Z]+', s):
|
|
Packit Service |
bbaf07 |
state = STATE_EXIT
|
|
Packit Service |
bbaf07 |
return opts
|
|
Packit Service |
bbaf07 |
|
|
Packit Service |
bbaf07 |
def format_code(s):
|
|
Packit Service |
bbaf07 |
# remove common indent in the block
|
|
Packit Service |
bbaf07 |
min_indent = None
|
|
Packit Service |
bbaf07 |
for ln in s.split("\n"):
|
|
Packit Service |
bbaf07 |
indent = 0
|
|
Packit Service |
bbaf07 |
for c in ln:
|
|
Packit Service |
bbaf07 |
if c == '\t': indent += 1
|
|
Packit Service |
bbaf07 |
else: break
|
|
Packit Service |
bbaf07 |
if min_indent is None:
|
|
Packit Service |
bbaf07 |
min_indent = indent
|
|
Packit Service |
bbaf07 |
elif indent > 0:
|
|
Packit Service |
bbaf07 |
min_indent = min(indent, min_indent)
|
|
Packit Service |
bbaf07 |
out = ''
|
|
Packit Service |
bbaf07 |
lines = s.split("\n")
|
|
Packit Service |
bbaf07 |
if lines[-1].strip() == '':
|
|
Packit Service |
bbaf07 |
lines.pop()
|
|
Packit Service |
bbaf07 |
for ln in lines:
|
|
Packit Service |
bbaf07 |
out += "| %s\n" % ln[min_indent:]
|
|
Packit Service |
bbaf07 |
return out
|
|
Packit Service |
bbaf07 |
|
|
Packit Service |
bbaf07 |
def sortedset(s):
|
|
Packit Service |
bbaf07 |
return sorted(list(s), key=lambda x: re.sub('^no', '', x))
|
|
Packit Service |
bbaf07 |
|
|
Packit Service |
bbaf07 |
def opt_neg(opt):
|
|
Packit Service |
bbaf07 |
if opt.startswith("no"):
|
|
Packit Service |
bbaf07 |
return opt[2:]
|
|
Packit Service |
bbaf07 |
else:
|
|
Packit Service |
bbaf07 |
return "no"+opt
|
|
Packit Service |
bbaf07 |
|
|
Packit Service |
bbaf07 |
def main():
|
|
Packit Service |
bbaf07 |
ap = argparse.ArgumentParser(description="Cross-check mount options from cifs.ko/man page")
|
|
Packit Service |
bbaf07 |
ap.add_argument("cfile", help="path to connect.c")
|
|
Packit Service |
bbaf07 |
ap.add_argument("rstfile", help="path to mount.cifs.rst")
|
|
Packit Service |
bbaf07 |
args = ap.parse_args()
|
|
Packit Service |
bbaf07 |
|
|
Packit Service |
bbaf07 |
fmt2enum, enum2code = extract_kernel_opts(args.cfile)
|
|
Packit Service |
bbaf07 |
manopts = extract_man_opts(args.rstfile)
|
|
Packit Service |
bbaf07 |
|
|
Packit Service |
bbaf07 |
kernel_opts_set = set(fmt2enum.keys())
|
|
Packit Service |
bbaf07 |
man_opts_set = set(manopts.keys())
|
|
Packit Service |
bbaf07 |
|
|
Packit Service |
bbaf07 |
def opt_alias_is_doc(o):
|
|
Packit Service |
bbaf07 |
enum = fmt2enum[o]['enum']
|
|
Packit Service |
bbaf07 |
aliases = []
|
|
Packit Service |
bbaf07 |
for k,v in fmt2enum.items():
|
|
Packit Service |
bbaf07 |
if k != o and v['enum'] == enum:
|
|
Packit Service |
bbaf07 |
if opt_is_doc(k):
|
|
Packit Service |
bbaf07 |
return k
|
|
Packit Service |
bbaf07 |
return None
|
|
Packit Service |
bbaf07 |
|
|
Packit Service |
bbaf07 |
def opt_exists(o):
|
|
Packit Service |
bbaf07 |
return o in fmt2enum
|
|
Packit Service |
bbaf07 |
|
|
Packit Service |
bbaf07 |
def opt_is_doc(o):
|
|
Packit Service |
bbaf07 |
return o in manopts
|
|
Packit Service |
bbaf07 |
|
|
Packit Service |
5e7916 |
print('DUPLICATED DOC OPTIONS')
|
|
Packit Service |
5e7916 |
print('======================')
|
|
Packit Service |
5e7916 |
|
|
Packit Service |
5e7916 |
for opt in sortedset(man_opts_set):
|
|
Packit Service |
5e7916 |
if len(manopts[opt]) > 1:
|
|
Packit Service |
5e7916 |
lines = ", ".join([str(x['ln']) for x in manopts[opt]])
|
|
Packit Service |
5e7916 |
print("OPTION %-20.20s (lines %s)"%(opt, lines))
|
|
Packit Service |
5e7916 |
print()
|
|
Packit Service |
bbaf07 |
|
|
Packit Service |
bbaf07 |
print('UNDOCUMENTED OPTIONS')
|
|
Packit Service |
bbaf07 |
print('====================')
|
|
Packit Service |
bbaf07 |
|
|
Packit Service |
bbaf07 |
undoc_opts = kernel_opts_set - man_opts_set
|
|
Packit Service |
bbaf07 |
# group opts and their negations together
|
|
Packit Service |
bbaf07 |
for opt in sortedset(undoc_opts):
|
|
Packit Service |
bbaf07 |
fmt = fmt2enum[opt]['fmt']
|
|
Packit Service |
bbaf07 |
enum = fmt2enum[opt]['enum']
|
|
Packit Service |
bbaf07 |
code = format_code(enum2code[enum])
|
|
Packit Service |
bbaf07 |
neg = opt_neg(opt)
|
|
Packit Service |
bbaf07 |
|
|
Packit Service |
bbaf07 |
if enum == 'Opt_ignore':
|
|
Packit Service |
bbaf07 |
print("# skipping %s (Opt_ignore)\n"%opt)
|
|
Packit Service |
bbaf07 |
continue
|
|
Packit Service |
bbaf07 |
|
|
Packit Service |
bbaf07 |
if opt_exists(neg) and opt_is_doc(neg):
|
|
Packit Service |
bbaf07 |
print("# skipping %s (%s is documented)\n"%(opt, neg))
|
|
Packit Service |
bbaf07 |
continue
|
|
Packit Service |
bbaf07 |
|
|
Packit Service |
bbaf07 |
alias = opt_alias_is_doc(opt)
|
|
Packit Service |
bbaf07 |
if alias:
|
|
Packit Service |
bbaf07 |
print("# skipping %s (alias %s is documented)\n"%(opt, alias))
|
|
Packit Service |
bbaf07 |
continue
|
|
Packit Service |
bbaf07 |
|
|
Packit Service |
bbaf07 |
print('OPTION %s ("%s" -> %s):\n%s'%(opt, fmt, enum, code))
|
|
Packit Service |
bbaf07 |
|
|
Packit Service |
bbaf07 |
print('')
|
|
Packit Service |
bbaf07 |
print('DOCUMENTED BUT NON-EXISTING OPTIONS')
|
|
Packit Service |
bbaf07 |
print('===================================')
|
|
Packit Service |
bbaf07 |
|
|
Packit Service |
bbaf07 |
unex_opts = man_opts_set - kernel_opts_set
|
|
Packit Service |
bbaf07 |
# group opts and their negations together
|
|
Packit Service |
bbaf07 |
for opt in sortedset(unex_opts):
|
|
Packit Service |
5e7916 |
man = manopts[opt][0]
|
|
Packit Service |
5e7916 |
print('OPTION %s ("%s") line %d' % (opt, man['fmt'], man['ln']))
|
|
Packit Service |
bbaf07 |
|
|
Packit Service |
bbaf07 |
|
|
Packit Service |
bbaf07 |
print('')
|
|
Packit Service |
bbaf07 |
print('NEGATIVE OPTIONS WITHOUT POSITIVE')
|
|
Packit Service |
bbaf07 |
print('=================================')
|
|
Packit Service |
bbaf07 |
|
|
Packit Service |
bbaf07 |
for opt in sortedset(kernel_opts_set):
|
|
Packit Service |
bbaf07 |
if not opt.startswith('no'):
|
|
Packit Service |
bbaf07 |
continue
|
|
Packit Service |
bbaf07 |
|
|
Packit Service |
bbaf07 |
neg = opt[2:]
|
|
Packit Service |
bbaf07 |
if not opt_exists(neg):
|
|
Packit Service |
bbaf07 |
print("OPTION %s exists but not %s"%(opt,neg))
|
|
Packit Service |
bbaf07 |
|
|
Packit Service |
bbaf07 |
# little helper to test AND store result at the same time so you can
|
|
Packit Service |
bbaf07 |
# do if/elsif easily instead of nesting them when you need to do
|
|
Packit Service |
bbaf07 |
# captures
|
|
Packit Service |
bbaf07 |
class RX:
|
|
Packit Service |
bbaf07 |
def __init__(self):
|
|
Packit Service |
bbaf07 |
pass
|
|
Packit Service |
bbaf07 |
def search(self, rx, s, flags=0):
|
|
Packit Service |
bbaf07 |
self.r = re.search(rx, s, flags)
|
|
Packit Service |
bbaf07 |
return self.r
|
|
Packit Service |
bbaf07 |
def group(self, n):
|
|
Packit Service |
bbaf07 |
return self.r.group(n)
|
|
Packit Service |
bbaf07 |
|
|
Packit Service |
bbaf07 |
if __name__ == '__main__':
|
|
Packit Service |
bbaf07 |
main()
|