Contents of /trunk/mkinitrd-magellan/klibc/usr/kinit/nfsmount/mount.c
Parent Directory | Revision Log
Revision 815 -
(show annotations)
(download)
Fri Apr 24 18:32:46 2009 UTC (15 years ago) by niro
File MIME type: text/plain
File size: 7991 byte(s)
Fri Apr 24 18:32:46 2009 UTC (15 years ago) by niro
File MIME type: text/plain
File size: 7991 byte(s)
-updated to klibc-1.5.15
1 | #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 | fprintf(stderr, "mount call failed - server replied: %s.\n", |
191 | strerror(ntohl(mnt_reply.status))); |
192 | 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 | } |