Annotation of /trunk/mkinitrd-magellan/klibc/usr/kinit/nfsmount/mount.c
Parent Directory | Revision Log
Revision 815 -
(hide annotations)
(download)
Fri Apr 24 18:32:46 2009 UTC (15 years, 1 month ago) by niro
File MIME type: text/plain
File size: 7991 byte(s)
Fri Apr 24 18:32:46 2009 UTC (15 years, 1 month ago) by niro
File MIME type: text/plain
File size: 7991 byte(s)
-updated to klibc-1.5.15
1 | niro | 532 | #include <sys/mount.h> |
2 | #include <sys/types.h> | ||
3 | #include <sys/socket.h> | ||
4 | #include <arpa/inet.h> | ||
5 | #include <netinet/in.h> | ||
6 | #include <linux/nfs.h> | ||
7 | #include <stdio.h> | ||
8 | #include <stdlib.h> | ||
9 | #include <string.h> | ||
10 | #include <errno.h> | ||
11 | |||
12 | #include "nfsmount.h" | ||
13 | #include "sunrpc.h" | ||
14 | |||
15 | static uint32_t mount_port; | ||
16 | |||
17 | struct mount_call { | ||
18 | struct rpc_call rpc; | ||
19 | uint32_t path_len; | ||
20 | char path[0]; | ||
21 | }; | ||
22 | |||
23 | /* | ||
24 | * The following structure is the NFS v3 on-the-wire file handle, | ||
25 | * as defined in rfc1813. | ||
26 | * This differs from the structure used by the kernel, | ||
27 | * defined in <linux/nfh3.h>: rfc has a long in network order, | ||
28 | * kernel has a short in native order. | ||
29 | * Both kernel and rfc use the name nfs_fh; kernel name is | ||
30 | * visible to user apps in some versions of libc. | ||
31 | * Use different name to avoid clashes. | ||
32 | */ | ||
33 | #define NFS_MAXFHSIZE_WIRE 64 | ||
34 | struct nfs_fh_wire { | ||
35 | uint32_t size; | ||
36 | char data[NFS_MAXFHSIZE_WIRE]; | ||
37 | } __attribute__ ((packed)); | ||
38 | |||
39 | struct mount_reply { | ||
40 | struct rpc_reply reply; | ||
41 | uint32_t status; | ||
42 | struct nfs_fh_wire fh; | ||
43 | } __attribute__ ((packed)); | ||
44 | |||
45 | #define MNT_REPLY_MINSIZE (sizeof(struct rpc_reply) + sizeof(uint32_t)) | ||
46 | |||
47 | static int get_ports(uint32_t server, const struct nfs_mount_data *data) | ||
48 | { | ||
49 | uint32_t nfs_ver, mount_ver; | ||
50 | uint32_t proto; | ||
51 | |||
52 | if (data->flags & NFS_MOUNT_VER3) { | ||
53 | nfs_ver = NFS3_VERSION; | ||
54 | mount_ver = NFS_MNT3_VERSION; | ||
55 | } else { | ||
56 | nfs_ver = NFS2_VERSION; | ||
57 | mount_ver = NFS_MNT_VERSION; | ||
58 | } | ||
59 | |||
60 | proto = (data->flags & NFS_MOUNT_TCP) ? IPPROTO_TCP : IPPROTO_UDP; | ||
61 | |||
62 | if (nfs_port == 0) { | ||
63 | nfs_port = portmap(server, NFS_PROGRAM, nfs_ver, proto); | ||
64 | if (nfs_port == 0) { | ||
65 | if (proto == IPPROTO_TCP) { | ||
66 | struct in_addr addr = { server }; | ||
67 | fprintf(stderr, "NFS over TCP not " | ||
68 | "available from %s\n", inet_ntoa(addr)); | ||
69 | return -1; | ||
70 | } | ||
71 | nfs_port = NFS_PORT; | ||
72 | } | ||
73 | } | ||
74 | |||
75 | if (mount_port == 0) { | ||
76 | mount_port = portmap(server, NFS_MNT_PROGRAM, mount_ver, proto); | ||
77 | if (mount_port == 0) | ||
78 | mount_port = MOUNT_PORT; | ||
79 | } | ||
80 | return 0; | ||
81 | } | ||
82 | |||
83 | static inline int pad_len(int len) | ||
84 | { | ||
85 | return (len + 3) & ~3; | ||
86 | } | ||
87 | |||
88 | static inline void dump_params(uint32_t server, | ||
89 | const char *path, | ||
90 | const struct nfs_mount_data *data) | ||
91 | { | ||
92 | (void)server; | ||
93 | (void)path; | ||
94 | (void)data; | ||
95 | |||
96 | #ifdef NFS_DEBUG | ||
97 | struct in_addr addr = { server }; | ||
98 | |||
99 | printf("NFS params:\n"); | ||
100 | printf(" server = %s, path = \"%s\", ", inet_ntoa(addr), path); | ||
101 | printf("version = %d, proto = %s\n", | ||
102 | data->flags & NFS_MOUNT_VER3 ? 3 : 2, | ||
103 | (data->flags & NFS_MOUNT_TCP) ? "tcp" : "udp"); | ||
104 | printf(" mount_port = %d, nfs_port = %d, flags = %08x\n", | ||
105 | mount_port, nfs_port, data->flags); | ||
106 | printf(" rsize = %d, wsize = %d, timeo = %d, retrans = %d\n", | ||
107 | data->rsize, data->wsize, data->timeo, data->retrans); | ||
108 | printf(" acreg (min, max) = (%d, %d), acdir (min, max) = (%d, %d)\n", | ||
109 | data->acregmin, data->acregmax, data->acdirmin, data->acdirmax); | ||
110 | printf(" soft = %d, intr = %d, posix = %d, nocto = %d, noac = %d\n", | ||
111 | (data->flags & NFS_MOUNT_SOFT) != 0, | ||
112 | (data->flags & NFS_MOUNT_INTR) != 0, | ||
113 | (data->flags & NFS_MOUNT_POSIX) != 0, | ||
114 | (data->flags & NFS_MOUNT_NOCTO) != 0, | ||
115 | (data->flags & NFS_MOUNT_NOAC) != 0); | ||
116 | #endif | ||
117 | } | ||
118 | |||
119 | static inline void dump_fh(const char *data, int len) | ||
120 | { | ||
121 | (void)data; | ||
122 | (void)len; | ||
123 | |||
124 | #ifdef NFS_DEBUG | ||
125 | int i = 0; | ||
126 | int max = len - (len % 8); | ||
127 | |||
128 | printf("Root file handle: %d bytes\n", NFS2_FHSIZE); | ||
129 | |||
130 | while (i < max) { | ||
131 | int j; | ||
132 | |||
133 | printf(" %4d: ", i); | ||
134 | for (j = 0; j < 4; j++) { | ||
135 | printf("%02x %02x %02x %02x ", | ||
136 | data[i] & 0xff, data[i + 1] & 0xff, | ||
137 | data[i + 2] & 0xff, data[i + 3] & 0xff); | ||
138 | } | ||
139 | i += j; | ||
140 | printf("\n"); | ||
141 | } | ||
142 | #endif | ||
143 | } | ||
144 | |||
145 | static struct mount_reply mnt_reply; | ||
146 | |||
147 | static int mount_call(uint32_t proc, uint32_t version, | ||
148 | const char *path, struct client *clnt) | ||
149 | { | ||
150 | struct mount_call *mnt_call = NULL; | ||
151 | size_t path_len, call_len; | ||
152 | struct rpc rpc; | ||
153 | int ret = 0; | ||
154 | |||
155 | path_len = strlen(path); | ||
156 | call_len = sizeof(*mnt_call) + pad_len(path_len); | ||
157 | |||
158 | if ((mnt_call = malloc(call_len)) == NULL) { | ||
159 | perror("malloc"); | ||
160 | goto bail; | ||
161 | } | ||
162 | |||
163 | memset(mnt_call, 0, sizeof(*mnt_call)); | ||
164 | |||
165 | mnt_call->rpc.program = htonl(NFS_MNT_PROGRAM); | ||
166 | mnt_call->rpc.prog_vers = htonl(version); | ||
167 | mnt_call->rpc.proc = htonl(proc); | ||
168 | mnt_call->path_len = htonl(path_len); | ||
169 | memcpy(mnt_call->path, path, path_len); | ||
170 | |||
171 | rpc.call = (struct rpc_call *)mnt_call; | ||
172 | rpc.call_len = call_len; | ||
173 | rpc.reply = (struct rpc_reply *)&mnt_reply; | ||
174 | rpc.reply_len = sizeof(mnt_reply); | ||
175 | |||
176 | if (rpc_call(clnt, &rpc) < 0) | ||
177 | goto bail; | ||
178 | |||
179 | if (proc != MNTPROC_MNT) { | ||
180 | goto done; | ||
181 | } | ||
182 | |||
183 | if (rpc.reply_len < MNT_REPLY_MINSIZE) { | ||
184 | fprintf(stderr, "incomplete reply: %zu < %zu\n", | ||
185 | rpc.reply_len, MNT_REPLY_MINSIZE); | ||
186 | goto bail; | ||
187 | } | ||
188 | |||
189 | if (mnt_reply.status != 0) { | ||
190 | niro | 815 | fprintf(stderr, "mount call failed - server replied: %s.\n", |
191 | strerror(ntohl(mnt_reply.status))); | ||
192 | niro | 532 | goto bail; |
193 | } | ||
194 | |||
195 | goto done; | ||
196 | |||
197 | bail: | ||
198 | ret = -1; | ||
199 | |||
200 | done: | ||
201 | if (mnt_call) { | ||
202 | free(mnt_call); | ||
203 | } | ||
204 | |||
205 | return ret; | ||
206 | } | ||
207 | |||
208 | static int mount_v2(const char *path, | ||
209 | struct nfs_mount_data *data, struct client *clnt) | ||
210 | { | ||
211 | int ret = mount_call(MNTPROC_MNT, NFS_MNT_VERSION, path, clnt); | ||
212 | |||
213 | if (ret == 0) { | ||
214 | dump_fh((const char *)&mnt_reply.fh, NFS2_FHSIZE); | ||
215 | |||
216 | data->root.size = NFS_FHSIZE; | ||
217 | memcpy(data->root.data, &mnt_reply.fh, NFS_FHSIZE); | ||
218 | memcpy(data->old_root.data, &mnt_reply.fh, NFS_FHSIZE); | ||
219 | } | ||
220 | |||
221 | return ret; | ||
222 | } | ||
223 | |||
224 | static inline int umount_v2(const char *path, struct client *clnt) | ||
225 | { | ||
226 | return mount_call(MNTPROC_UMNT, NFS_MNT_VERSION, path, clnt); | ||
227 | } | ||
228 | |||
229 | static int mount_v3(const char *path, | ||
230 | struct nfs_mount_data *data, struct client *clnt) | ||
231 | { | ||
232 | int ret = mount_call(MNTPROC_MNT, NFS_MNT3_VERSION, path, clnt); | ||
233 | |||
234 | if (ret == 0) { | ||
235 | size_t fhsize = ntohl(mnt_reply.fh.size); | ||
236 | |||
237 | dump_fh((const char *)&mnt_reply.fh.data, fhsize); | ||
238 | |||
239 | memset(data->old_root.data, 0, NFS_FHSIZE); | ||
240 | memset(&data->root, 0, sizeof(data->root)); | ||
241 | data->root.size = fhsize; | ||
242 | memcpy(&data->root.data, mnt_reply.fh.data, fhsize); | ||
243 | data->flags |= NFS_MOUNT_VER3; | ||
244 | } | ||
245 | |||
246 | return ret; | ||
247 | } | ||
248 | |||
249 | static inline int umount_v3(const char *path, struct client *clnt) | ||
250 | { | ||
251 | return mount_call(MNTPROC_UMNT, NFS_MNT3_VERSION, path, clnt); | ||
252 | } | ||
253 | |||
254 | int nfs_mount(const char *pathname, const char *hostname, | ||
255 | uint32_t server, const char *rem_path, const char *path, | ||
256 | struct nfs_mount_data *data) | ||
257 | { | ||
258 | struct client *clnt = NULL; | ||
259 | struct sockaddr_in addr; | ||
260 | char mounted = 0; | ||
261 | int sock = -1; | ||
262 | int ret = 0; | ||
263 | int mountflags; | ||
264 | |||
265 | if (get_ports(server, data) != 0) { | ||
266 | goto bail; | ||
267 | } | ||
268 | |||
269 | dump_params(server, rem_path, data); | ||
270 | |||
271 | if (data->flags & NFS_MOUNT_TCP) { | ||
272 | clnt = tcp_client(server, mount_port, CLI_RESVPORT); | ||
273 | } else { | ||
274 | clnt = udp_client(server, mount_port, CLI_RESVPORT); | ||
275 | } | ||
276 | |||
277 | if (clnt == NULL) { | ||
278 | goto bail; | ||
279 | } | ||
280 | |||
281 | if (data->flags & NFS_MOUNT_VER3) { | ||
282 | ret = mount_v3(rem_path, data, clnt); | ||
283 | } else { | ||
284 | ret = mount_v2(rem_path, data, clnt); | ||
285 | } | ||
286 | |||
287 | if (ret == -1) { | ||
288 | goto bail; | ||
289 | } | ||
290 | mounted = 1; | ||
291 | |||
292 | if (data->flags & NFS_MOUNT_TCP) { | ||
293 | sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); | ||
294 | } else { | ||
295 | sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); | ||
296 | } | ||
297 | |||
298 | if (sock == -1) { | ||
299 | perror("socket"); | ||
300 | goto bail; | ||
301 | } | ||
302 | |||
303 | if (bindresvport(sock, 0) == -1) { | ||
304 | perror("bindresvport"); | ||
305 | goto bail; | ||
306 | } | ||
307 | |||
308 | addr.sin_family = AF_INET; | ||
309 | addr.sin_addr.s_addr = server; | ||
310 | addr.sin_port = htons(nfs_port); | ||
311 | memcpy(&data->addr, &addr, sizeof(data->addr)); | ||
312 | |||
313 | strncpy(data->hostname, hostname, sizeof(data->hostname)); | ||
314 | |||
315 | data->fd = sock; | ||
316 | |||
317 | mountflags = (data->flags & NFS_MOUNT_KLIBC_RONLY) ? MS_RDONLY : 0; | ||
318 | data->flags = data->flags & NFS_MOUNT_FLAGMASK; | ||
319 | ret = mount(pathname, path, "nfs", mountflags, data); | ||
320 | |||
321 | if (ret == -1) { | ||
322 | if (errno == ENODEV) { | ||
323 | fprintf(stderr, "mount: the kernel lacks NFS v%d " | ||
324 | "support\n", | ||
325 | (data->flags & NFS_MOUNT_VER3) ? 3 : 2); | ||
326 | } else { | ||
327 | perror("mount"); | ||
328 | } | ||
329 | goto bail; | ||
330 | } | ||
331 | |||
332 | DEBUG(("Mounted %s on %s\n", pathname, path)); | ||
333 | |||
334 | goto done; | ||
335 | |||
336 | bail: | ||
337 | if (mounted) { | ||
338 | if (data->flags & NFS_MOUNT_VER3) { | ||
339 | umount_v3(path, clnt); | ||
340 | } else { | ||
341 | umount_v2(path, clnt); | ||
342 | } | ||
343 | } | ||
344 | |||
345 | ret = -1; | ||
346 | |||
347 | done: | ||
348 | if (clnt) { | ||
349 | client_free(clnt); | ||
350 | } | ||
351 | |||
352 | if (sock != -1) { | ||
353 | close(sock); | ||
354 | } | ||
355 | |||
356 | return ret; | ||
357 | } |