|
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 |
|
9 import rpc |
|
10 from rpc import UDPClient, TCPClient |
|
11 from mountclient import FHSIZE, MountPacker, MountUnpacker |
|
12 |
|
13 NFS_PROGRAM = 100003 |
|
14 NFS_VERSION = 2 |
|
15 |
|
16 # enum stat |
|
17 NFS_OK = 0 |
|
18 # (...many error values...) |
|
19 |
|
20 # enum ftype |
|
21 NFNON = 0 |
|
22 NFREG = 1 |
|
23 NFDIR = 2 |
|
24 NFBLK = 3 |
|
25 NFCHR = 4 |
|
26 NFLNK = 5 |
|
27 |
|
28 |
|
29 class 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 |
|
62 class 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 |
|
122 class 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 |
|
179 def 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) |