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