--- /dev/null
+from afs cimport *
+from afs import pyafs_error
+
+cdef import from "afs/prs_fs.h":
+ enum:
+ PRSFS_READ, PRSFS_WRITE, PRSFS_INSERT, PRSFS_LOOKUP,
+ PRSFS_DELETE, PRSFS_LOCK, PRSFS_ADMINISTER,
+ PRSFS_USR0, PRSFS_USR1, PRSFS_USR2, PRSFS_USR2, PRSFS_USR3,
+ PRSFS_USR4, PRSFS_USR5, PRSFS_USR6, PRSFS_USR7
+
+# This is defined in afs/afs.h, but I can't figure how to include the
+# header. Also, venus/fs.c redefines the struct, so why not!
+cdef struct vcxstat2:
+ afs_int32 callerAccess
+ afs_int32 cbExpires
+ afs_int32 anyAccess
+ char mvstat
+
+READ = PRSFS_READ
+WRITE = PRSFS_WRITE
+INSERT = PRSFS_INSERT
+LOOKUP = PRSFS_LOOKUP
+DELETE = PRSFS_DELETE
+LOCK = PRSFS_LOCK
+ADMINISTER = PRSFS_ADMINISTER
+USR0 = PRSFS_USR0
+USR1 = PRSFS_USR1
+USR2 = PRSFS_USR2
+USR3 = PRSFS_USR3
+USR4 = PRSFS_USR4
+USR5 = PRSFS_USR5
+USR6 = PRSFS_USR6
+USR7 = PRSFS_USR7
+
+DEF MAXSIZE = 2048
+
+def getAcl(char* dir, int follow=1):
+ cdef char space[MAXSIZE]
+ pioctl_read(dir, VIOCGETAL, space, MAXSIZE, follow)
+ return space
+
+def getCallerAccess(char *dir, int follow=1):
+ cdef vcxstat2 stat
+ pioctl_read(dir, VIOC_GETVCXSTATUS2, <void*>&stat, sizeof(vcxstat2), follow)
+ return stat.callerAccess
--- /dev/null
+import _acl
+from _acl import READ, WRITE, INSERT, LOOKUP, DELETE, LOCK, ADMINISTER, \
+ USR0, USR1, USR2, USR3, USR4, USR5, USR6, USR7
+from _acl import getCallerAccess
+
+_canonical = {
+ "read": "rl",
+ "write": "rwlidwk",
+ "all": "rwlidwka",
+ "mail": "lik",
+ "none": "",
+}
+
+_charBitAssoc = [
+ ('r', READ),
+ ('w', WRITE),
+ ('i', INSERT),
+ ('l', LOOKUP),
+ ('d', DELETE),
+ ('k', LOCK),
+ ('a', ADMINISTER),
+ ('A', USR0),
+ ('B', USR1),
+ ('C', USR2),
+ ('D', USR3),
+ ('E', USR4),
+ ('F', USR5),
+ ('G', USR6),
+ ('H', USR7),
+]
+
+_char2bit = dict(_charBitAssoc)
+
+
+def readRights(s):
+ """Canonicalizes string rights to bitmask"""
+ if s in _canonical: s = _canonical[s]
+ return _parseRights(s)
+
+def showRights(r):
+ """Takes a bitmask and returns a rwlidka string"""
+ s = ""
+ for char,mask in _charBitAssoc:
+ if r & mask == mask: s += char
+ return s
+
+def _parseRights(s):
+ """Parses a rwlid... rights tring to bitmask"""
+ r = 0
+ try:
+ for c in s:
+ r = r | _char2bit[c]
+ except KeyError:
+ raise ValueError
+ return r
+
+def _parseAcl(inp):
+ lines = inp.split("\n")
+ npos = int(lines[0].split(" ")[0])
+ pos = {}
+ neg = {}
+ for l in lines[2:]:
+ if l == "": continue
+ name, acl = l.split()
+ if npos:
+ npos -= 1
+ pos[name] = int(acl)
+ else:
+ # negative acl
+ neg[name] = int(acl)
+ return (pos, neg)
+
+class ACL(object):
+ def __init__(self, pos, neg):
+ """
+ ``pos``
+ Dictionary of usernames to positive ACL bitmasks
+ ``neg``
+ Dictionary of usernames to negative ACL bitmasks
+ """
+ self.pos = pos
+ self.neg = neg
+ @staticmethod
+ def retrieve(dir, follow=1):
+ """Retrieve the ACL for an AFS directory"""
+ pos, neg = _parseAcl(_acl.getAcl(dir, follow))
+ return ACL(pos, neg)
+
void initialize_RXK_error_table()
cdef extern from "ubik.h":
void initialize_U_error_table()
+
+cdef extern from "afs/vice.h":
+ struct ViceIoctl:
+ void *cin "in"
+ void *out
+ unsigned short out_size
+ unsigned short in_size
+
+cdef import from "afs/venus.h":
+ enum:
+ # PIOCTLS to Venus that we use
+ VIOCGETAL, VIOC_GETVCXSTATUS2
+
+# pioctl doesn't actually have a header, so we have to define it here
+cdef extern int pioctl(char *, afs_int32, ViceIoctl *, afs_int32)
+cdef int pioctl_read(char *, afs_int32, void *, unsigned short, afs_int32) except -1
+
import sys
+# otherwise certain headers are unhappy
+cdef import from "netinet/in.h": pass
+cdef import from "afs/vice.h": pass
+
cdef int _init = 0
+# pioctl convenience wrappers
+
+cdef extern int pioctl_read(char *dir, afs_int32 op, void *buffer, unsigned short size, afs_int32 follow) except -1:
+ cdef ViceIoctl blob
+ cdef afs_int32 code
+ blob.in_size = 0
+ blob.out_size = size
+ blob.out = buffer
+ code = pioctl(dir, op, &blob, follow)
+ # This might work with the rest of OpenAFS, but I'm not convinced
+ # the rest of it is consistent
+ if code == -1:
+ raise OSError(errno, strerror(errno))
+ pyafs_error(code)
+ return code
+
+# Error handling
+
class AFSException(Exception):
def __init__(self, errno):
self.errno = errno
--- /dev/null
+import nose
+import afs.acl as acl
+
+def test_showRights():
+ assert acl.showRights(acl.READ | acl.WRITE) == "rw"
+
+def test_crights():
+ assert acl.crights('read') & acl.READ
+ assert acl.crights('read') & acl.LOOKUP
+ assert not acl.crights('read') & acl.WRITE
+
+def test_retrieve():
+ assert acl.ACL.retrieve('/afs/athena.mit.edu/contrib/bitbucket2').pos['system:anyuser'] & acl.WRITE
+ assert acl.ACL.retrieve('/afs/athena.mit.edu/user/t/a/tabbott').neg['yuranlu'] & acl.USR0
+
+def test_getCallerAccess():
+ assert acl.getCallerAccess('/afs/athena.mit.edu/contrib/bitbucket2') & acl.WRITE
+
+if __name__ == '__main__':
+ nose.main()
+
ext_modules=[
PyAFSExtension("afs.afs"),
PyAFSExtension("afs._pts"),
+ PyAFSExtension("afs._acl"),
],
cmdclass= {"build_ext": build_ext}
)