1# NFS RPC client -- RFC 1094
2
3# XXX This is not yet complete.
4# XXX Only GETATTR, SETTTR, LOOKUP and READDIR are supported.
5
6# (See mountclient.py for some hints on how to write RPC clients in
7# Python in general)
8
9import rpc
10from rpc import UDPClient, TCPClient
11from mountclient import FHSIZE, MountPacker, MountUnpacker
12
13NFS_PROGRAM = 100003
14NFS_VERSION = 2
15
16# enum stat
17NFS_OK = 0
18# (...many error values...)
19
20# enum ftype
21NFNON = 0
22NFREG = 1
23NFDIR = 2
24NFBLK = 3
25NFCHR = 4
26NFLNK = 5
27
28
29class NFSPacker(MountPacker):
30
31    def pack_sattrargs(self, sa):
32        file, attributes = sa
33        self.pack_fhandle(file)
34        self.pack_sattr(attributes)
35
36    def pack_sattr(self, sa):
37        mode, uid, gid, size, atime, mtime = sa
38        self.pack_uint(mode)
39        self.pack_uint(uid)
40        self.pack_uint(gid)
41        self.pack_uint(size)
42        self.pack_timeval(atime)
43        self.pack_timeval(mtime)
44
45    def pack_diropargs(self, da):
46        dir, name = da
47        self.pack_fhandle(dir)
48        self.pack_string(name)
49
50    def pack_readdirargs(self, ra):
51        dir, cookie, count = ra
52        self.pack_fhandle(dir)
53        self.pack_uint(cookie)
54        self.pack_uint(count)
55
56    def pack_timeval(self, tv):
57        secs, usecs = tv
58        self.pack_uint(secs)
59        self.pack_uint(usecs)
60
61
62class NFSUnpacker(MountUnpacker):
63
64    def unpack_readdirres(self):
65        status = self.unpack_enum()
66        if status == NFS_OK:
67            entries = self.unpack_list(self.unpack_entry)
68            eof = self.unpack_bool()
69            rest = (entries, eof)
70        else:
71            rest = None
72        return (status, rest)
73
74    def unpack_entry(self):
75        fileid = self.unpack_uint()
76        name = self.unpack_string()
77        cookie = self.unpack_uint()
78        return (fileid, name, cookie)
79
80    def unpack_diropres(self):
81        status = self.unpack_enum()
82        if status == NFS_OK:
83            fh = self.unpack_fhandle()
84            fa = self.unpack_fattr()
85            rest = (fh, fa)
86        else:
87            rest = None
88        return (status, rest)
89
90    def unpack_attrstat(self):
91        status = self.unpack_enum()
92        if status == NFS_OK:
93            attributes = self.unpack_fattr()
94        else:
95            attributes = None
96        return status, attributes
97
98    def unpack_fattr(self):
99        type = self.unpack_enum()
100        mode = self.unpack_uint()
101        nlink = self.unpack_uint()
102        uid = self.unpack_uint()
103        gid = self.unpack_uint()
104        size = self.unpack_uint()
105        blocksize = self.unpack_uint()
106        rdev = self.unpack_uint()
107        blocks = self.unpack_uint()
108        fsid = self.unpack_uint()
109        fileid = self.unpack_uint()
110        atime = self.unpack_timeval()
111        mtime = self.unpack_timeval()
112        ctime = self.unpack_timeval()
113        return (type, mode, nlink, uid, gid, size, blocksize, \
114                rdev, blocks, fsid, fileid, atime, mtime, ctime)
115
116    def unpack_timeval(self):
117        secs = self.unpack_uint()
118        usecs = self.unpack_uint()
119        return (secs, usecs)
120
121
122class NFSClient(UDPClient):
123
124    def __init__(self, host):
125        UDPClient.__init__(self, host, NFS_PROGRAM, NFS_VERSION)
126
127    def addpackers(self):
128        self.packer = NFSPacker()
129        self.unpacker = NFSUnpacker('')
130
131    def mkcred(self):
132        if self.cred is None:
133            self.cred = rpc.AUTH_UNIX, rpc.make_auth_unix_default()
134        return self.cred
135
136    def Getattr(self, fh):
137        return self.make_call(1, fh, \
138                self.packer.pack_fhandle, \
139                self.unpacker.unpack_attrstat)
140
141    def Setattr(self, sa):
142        return self.make_call(2, sa, \
143                self.packer.pack_sattrargs, \
144                self.unpacker.unpack_attrstat)
145
146    # Root() is obsolete
147
148    def Lookup(self, da):
149        return self.make_call(4, da, \
150                self.packer.pack_diropargs, \
151                self.unpacker.unpack_diropres)
152
153    # ...
154
155    def Readdir(self, ra):
156        return self.make_call(16, ra, \
157                self.packer.pack_readdirargs, \
158                self.unpacker.unpack_readdirres)
159
160    # Shorthand to get the entire contents of a directory
161    def Listdir(self, dir):
162        list = []
163        ra = (dir, 0, 2000)
164        while 1:
165            (status, rest) = self.Readdir(ra)
166            if status <> NFS_OK:
167                break
168            entries, eof = rest
169            last_cookie = None
170            for fileid, name, cookie in entries:
171                list.append((fileid, name))
172                last_cookie = cookie
173            if eof or last_cookie is None:
174                break
175            ra = (ra[0], last_cookie, ra[2])
176        return list
177
178
179def test():
180    import sys
181    if sys.argv[1:]: host = sys.argv[1]
182    else: host = ''
183    if sys.argv[2:]: filesys = sys.argv[2]
184    else: filesys = None
185    from mountclient import UDPMountClient, TCPMountClient
186    mcl = TCPMountClient(host)
187    if filesys is None:
188        list = mcl.Export()
189        for item in list:
190            print item
191        return
192    sf = mcl.Mnt(filesys)
193    print sf
194    fh = sf[1]
195    if fh:
196        ncl = NFSClient(host)
197        attrstat = ncl.Getattr(fh)
198        print attrstat
199        list = ncl.Listdir(fh)
200        for item in list: print item
201        mcl.Umnt(filesys)
202