Contents of /trunk/mkinitrd-magellan/klibc/usr/kinit/nfsmount/mount.c
Parent Directory | Revision Log
Revision 906 -
(show annotations)
(download)
Fri Oct 16 11:37:14 2009 UTC (14 years, 11 months ago) by niro
File MIME type: text/plain
File size: 8086 byte(s)
Fri Oct 16 11:37:14 2009 UTC (14 years, 11 months ago) by niro
File MIME type: text/plain
File size: 8086 byte(s)
-fixed missing mntproc definitions with newer kernels (>= 2.6.31)
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 | #ifndef MNTPROC_MNT |
48 | # define MNTPROC_MNT 1 |
49 | # define MNTPROC_UMNT 3 |
50 | #endif /* MNTPROC_MNT */ |
51 | |
52 | 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 | #ifdef NFS_DEBUG |
102 | 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 | #ifdef NFS_DEBUG |
130 | 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 | fprintf(stderr, "mount call failed - server replied: %s.\n", |
196 | strerror(ntohl(mnt_reply.status))); |
197 | 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 | DEBUG(("Mounted %s on %s\n", pathname, path)); |
338 | |
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 | } |