1
2 /*
3 * UNFS3 NFS protocol procedures
4 * (C) 2004, Pascal Schmidt
5 * see file LICENSE for license details
6 */
7
8 #include "config.h"
9
10 #include <sys/types.h>
11 #include <sys/stat.h>
12 #include <sys/param.h> /* needed for statfs() on NetBSD */
13 #if HAVE_SYS_MOUNT_H == 1
14 #include <sys/mount.h> /* dito */
15 #endif
16 #if HAVE_SYS_VMOUNT_H == 1
17 #include <sys/vmount.h> /* AIX */
18 #endif
19 #include <rpc/rpc.h>
20 #include <errno.h>
21 #include <fcntl.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <unistd.h>
26 #include <utime.h>
27 #ifndef WIN32
28 #include <sys/socket.h>
29 #include <sys/un.h>
30 #endif /* WIN32 */
31
32 #if HAVE_STATVFS == 1
33 # include <sys/statvfs.h>
34 #else
35 # define statvfs statfs
36 #endif
37
38 #include "nfs.h"
39 #include "mount.h"
40 #include "fh.h"
41 #include "fh_cache.h"
42 #include "attr.h"
43 #include "readdir.h"
44 #include "user.h"
45 #include "error.h"
46 #include "fd_cache.h"
47 #include "daemon.h"
48 #include "backend.h"
49 #include "Config/exports.h"
50 #include "Extras/cluster.h"
51
52 /*
53 * decompose filehandle and switch user if permitted access
54 * otherwise zero result structure and return with error status
55 */
56 #define PREP(p,f) do { \
57 unfs3_fh_t *fh = (void *)f.data.data_val; \
58 switch_to_root(); \
59 p = fh_decomp(f); \
60 if (exports_options(p, rqstp, NULL, NULL) == -1) { \
61 memset(&result, 0, sizeof(result)); \
62 if (p) \
63 result.status = NFS3ERR_ACCES; \
64 else \
65 result.status = NFS3ERR_STALE; \
66 return &result; \
67 } \
68 if (fh->pwhash != export_password_hash) { \
69 memset(&result, 0, sizeof(result)); \
70 result.status = NFS3ERR_STALE; \
71 return &result; \
72 } \
73 switch_user(rqstp); \
74 } while (0)
75
76 /*
77 * cat an object name onto a path, checking for illegal input
78 */
cat_name(const char * path,const char * name,char * result)79 nfsstat3 cat_name(const char *path, const char *name, char *result)
80 {
81 char *last;
82
83 if (!path)
84 return NFS3ERR_STALE;
85
86 if (!name)
87 return NFS3ERR_ACCES;
88
89 if (name[0] == 0 || strchr(name, '/') != NULL)
90 return NFS3ERR_ACCES;
91
92 if (strlen(path) + strlen(name) + 2 > NFS_MAXPATHLEN)
93 return NFS3ERR_NAMETOOLONG;
94
95 if (strcmp(name, ".") == 0) {
96 strcpy(result, path);
97 return NFS3_OK;
98 }
99
100 /*
101 * Irix clients do lookups for .. and then use the
102 * resulting filehandle for more lookups, causing them
103 * to get filehandles that fh_decomp_raw will refuse to
104 * resolve. Export list handling will also get very
105 * confused if we allow such filehandles.
106 */
107 if (strcmp(name, "..") == 0) {
108 last = strrchr(path, '/');
109 if (!last || last == path)
110 strcpy(result, "/");
111 else {
112 *last = 0;
113 strcpy(result, path);
114 *last = '/';
115 }
116 return NFS3_OK;
117 }
118
119 sprintf(result, "%s/%s", path, name);
120 return NFS3_OK;
121 }
122
nfsproc3_null_3_svc(U (void * argp),U (struct svc_req * rqstp))123 void *nfsproc3_null_3_svc(U(void *argp), U(struct svc_req *rqstp))
124 {
125 static void *result = NULL;
126
127 return &result;
128 }
129
nfsproc3_getattr_3_svc(GETATTR3args * argp,struct svc_req * rqstp)130 GETATTR3res *nfsproc3_getattr_3_svc(GETATTR3args * argp,
131 struct svc_req * rqstp)
132 {
133 static GETATTR3res result;
134 char *path;
135 post_op_attr post;
136
137 PREP(path, argp->object);
138 post = get_post_cached(rqstp);
139
140 result.status = NFS3_OK;
141 result.GETATTR3res_u.resok.obj_attributes =
142 post.post_op_attr_u.attributes;
143
144 return &result;
145 }
146
147 /*
148 * check ctime guard for SETATTR procedure
149 */
in_sync(sattrguard3 guard,pre_op_attr pre)150 static nfsstat3 in_sync(sattrguard3 guard, pre_op_attr pre)
151 {
152 if (!pre.attributes_follow)
153 return NFS3ERR_STALE;
154
155 if (!guard.check)
156 return NFS3_OK;
157
158 if (guard.sattrguard3_u.obj_ctime.seconds !=
159 pre.pre_op_attr_u.attributes.ctime.seconds)
160 return NFS3ERR_NOT_SYNC;
161
162 return NFS3_OK;
163 }
164
nfsproc3_setattr_3_svc(SETATTR3args * argp,struct svc_req * rqstp)165 SETATTR3res *nfsproc3_setattr_3_svc(SETATTR3args * argp,
166 struct svc_req * rqstp)
167 {
168 static SETATTR3res result;
169 pre_op_attr pre;
170 char *path;
171
172 PREP(path, argp->object);
173 pre = get_pre_cached();
174 result.status = join(in_sync(argp->guard, pre), exports_rw());
175
176 if (result.status == NFS3_OK)
177 result.status = set_attr(path, argp->object, argp->new_attributes);
178
179 /* overlaps with resfail */
180 result.SETATTR3res_u.resok.obj_wcc.before = pre;
181 result.SETATTR3res_u.resok.obj_wcc.after = get_post_stat(path, rqstp);
182
183 return &result;
184 }
185
nfsproc3_lookup_3_svc(LOOKUP3args * argp,struct svc_req * rqstp)186 LOOKUP3res *nfsproc3_lookup_3_svc(LOOKUP3args * argp, struct svc_req * rqstp)
187 {
188 static LOOKUP3res result;
189 unfs3_fh_t *fh;
190 char *path;
191 char obj[NFS_MAXPATHLEN];
192 backend_statstruct buf;
193 int res;
194 uint32 gen;
195
196 PREP(path, argp->what.dir);
197 result.status = cat_name(path, argp->what.name, obj);
198
199 cluster_lookup(obj, rqstp, &result.status);
200
201 if (result.status == NFS3_OK) {
202 res = backend_lstat(obj, &buf);
203 if (res == -1)
204 result.status = lookup_err();
205 else {
206 if (strcmp(argp->what.name, ".") == 0 ||
207 strcmp(argp->what.name, "..") == 0) {
208 fh = fh_comp_ptr(obj, rqstp, 0);
209 } else {
210 gen = backend_get_gen(buf, FD_NONE, obj);
211 fh = fh_extend(argp->what.dir, buf.st_dev, buf.st_ino, gen);
212 fh_cache_add(buf.st_dev, buf.st_ino, obj);
213 }
214
215 if (fh) {
216 result.LOOKUP3res_u.resok.object.data.data_len =
217 fh_length(fh);
218 result.LOOKUP3res_u.resok.object.data.data_val = (char *) fh;
219 result.LOOKUP3res_u.resok.obj_attributes =
220 get_post_buf(buf, rqstp);
221 } else {
222 /* path was too long */
223 result.status = NFS3ERR_NAMETOOLONG;
224 }
225 }
226 }
227
228 /* overlaps with resfail */
229 result.LOOKUP3res_u.resok.dir_attributes = get_post_stat(path, rqstp);
230
231 return &result;
232 }
233
nfsproc3_access_3_svc(ACCESS3args * argp,struct svc_req * rqstp)234 ACCESS3res *nfsproc3_access_3_svc(ACCESS3args * argp, struct svc_req * rqstp)
235 {
236 static ACCESS3res result;
237 char *path;
238 post_op_attr post;
239 mode_t mode;
240 int access = 0;
241
242 PREP(path, argp->object);
243 post = get_post_cached(rqstp);
244 mode = post.post_op_attr_u.attributes.mode;
245
246 /* owner permissions */
247 if (is_owner(st_cache.st_uid, rqstp)) {
248 if (mode & S_IRUSR)
249 access |= ACCESS3_READ;
250 if (mode & S_IWUSR)
251 access |= ACCESS3_MODIFY | ACCESS3_EXTEND;
252 if (mode & S_IXUSR) {
253 access |= ACCESS3_EXECUTE;
254 if (opt_readable_executables)
255 access |= ACCESS3_READ;
256 }
257 } else if (has_group(st_cache.st_gid, rqstp)) {
258 /* group permissions */
259 if (mode & S_IRGRP)
260 access |= ACCESS3_READ;
261 if (mode & S_IWGRP)
262 access |= ACCESS3_MODIFY | ACCESS3_EXTEND;
263 if (mode & S_IXGRP) {
264 access |= ACCESS3_EXECUTE;
265 if (opt_readable_executables)
266 access |= ACCESS3_READ;
267 }
268 } else {
269 /* other permissions */
270 if (mode & S_IROTH)
271 access |= ACCESS3_READ;
272 if (mode & S_IWOTH)
273 access |= ACCESS3_MODIFY | ACCESS3_EXTEND;
274 if (mode & S_IXOTH) {
275 access |= ACCESS3_EXECUTE;
276 if (opt_readable_executables)
277 access |= ACCESS3_READ;
278 }
279 }
280
281 /* root is allowed everything */
282 if (get_uid(rqstp) == 0)
283 access |= ACCESS3_READ | ACCESS3_MODIFY | ACCESS3_EXTEND;
284
285 /* adjust if directory */
286 if (post.post_op_attr_u.attributes.type == NF3DIR) {
287 if (access & (ACCESS3_READ | ACCESS3_EXECUTE))
288 access |= ACCESS3_LOOKUP;
289 if (access & ACCESS3_MODIFY)
290 access |= ACCESS3_DELETE;
291 access &= ~ACCESS3_EXECUTE;
292 }
293
294 result.status = NFS3_OK;
295 result.ACCESS3res_u.resok.access = access & argp->access;
296 result.ACCESS3res_u.resok.obj_attributes = post;
297
298 return &result;
299 }
300
nfsproc3_readlink_3_svc(READLINK3args * argp,struct svc_req * rqstp)301 READLINK3res *nfsproc3_readlink_3_svc(READLINK3args * argp,
302 struct svc_req * rqstp)
303 {
304 static READLINK3res result;
305 char *path;
306 static char buf[NFS_MAXPATHLEN];
307 int res;
308
309 PREP(path, argp->symlink);
310
311 res = backend_readlink(path, buf, NFS_MAXPATHLEN - 1);
312 if (res == -1)
313 result.status = readlink_err();
314 else {
315 /* readlink does not NULL-terminate */
316 buf[res] = 0;
317
318 result.status = NFS3_OK;
319 result.READLINK3res_u.resok.data = buf;
320 }
321
322 /* overlaps with resfail */
323 result.READLINK3res_u.resok.symlink_attributes =
324 get_post_stat(path, rqstp);
325
326 return &result;
327 }
328
nfsproc3_read_3_svc(READ3args * argp,struct svc_req * rqstp)329 READ3res *nfsproc3_read_3_svc(READ3args * argp, struct svc_req * rqstp)
330 {
331 static READ3res result;
332 char *path;
333 int fd, res;
334 static char buf[NFS_MAXDATA_TCP + 1];
335 unsigned int maxdata;
336
337 if (get_socket_type(rqstp) == SOCK_STREAM)
338 maxdata = NFS_MAXDATA_TCP;
339 else
340 maxdata = NFS_MAXDATA_UDP;
341
342 PREP(path, argp->file);
343 result.status = is_reg();
344
345 /* handle reading of executables */
346 read_executable(rqstp, st_cache);
347
348 /* handle read of owned files */
349 read_by_owner(rqstp, st_cache);
350
351 /* if bigger than rtmax, truncate length */
352 if (argp->count > maxdata)
353 argp->count = maxdata;
354
355 if (result.status == NFS3_OK) {
356 fd = fd_open(path, argp->file, UNFS3_FD_READ, TRUE);
357 if (fd != -1) {
358 /* read one more to check for eof */
359 res = backend_pread(fd, buf, argp->count + 1, argp->offset);
360
361 /* eof if we could not read one more */
362 result.READ3res_u.resok.eof = (res <= (int64) argp->count);
363
364 /* close for real when hitting eof */
365 if (result.READ3res_u.resok.eof)
366 fd_close(fd, UNFS3_FD_READ, FD_CLOSE_REAL);
367 else {
368 fd_close(fd, UNFS3_FD_READ, FD_CLOSE_VIRT);
369 res--;
370 }
371
372 if (res >= 0) {
373 result.READ3res_u.resok.count = res;
374 result.READ3res_u.resok.data.data_len = res;
375 result.READ3res_u.resok.data.data_val = buf;
376 } else {
377 /* error during read() */
378
379 /* EINVAL means unreadable object */
380 if (errno == EINVAL)
381 result.status = NFS3ERR_INVAL;
382 else
383 result.status = NFS3ERR_IO;
384 }
385 } else
386 /* opening for read failed */
387 result.status = read_err();
388 }
389
390 /* overlaps with resfail */
391 result.READ3res_u.resok.file_attributes = get_post_stat(path, rqstp);
392
393 return &result;
394 }
395
nfsproc3_write_3_svc(WRITE3args * argp,struct svc_req * rqstp)396 WRITE3res *nfsproc3_write_3_svc(WRITE3args * argp, struct svc_req * rqstp)
397 {
398 static WRITE3res result;
399 char *path;
400 int fd, res, res_close;
401
402 PREP(path, argp->file);
403 result.status = join(is_reg(), exports_rw());
404
405 /* handle write of owned files */
406 write_by_owner(rqstp, st_cache);
407
408 if (result.status == NFS3_OK) {
409 /* We allow caching of the fd only for unstable writes. This is to
410 prevent generating a new write verifier for failed stable writes,
411 when the fd was not in the cache. Besides, for stable writes, the
412 fd will be removed from the cache by fd_close() below, so adding
413 it to and removing it from the cache is just a waste of CPU cycles
414 */
415 fd = fd_open(path, argp->file, UNFS3_FD_WRITE,
416 (argp->stable == UNSTABLE));
417 if (fd != -1) {
418 res =
419 backend_pwrite(fd, argp->data.data_val, argp->data.data_len,
420 argp->offset);
421
422 /* close for real if not UNSTABLE write */
423 if (argp->stable == UNSTABLE)
424 res_close = fd_close(fd, UNFS3_FD_WRITE, FD_CLOSE_VIRT);
425 else
426 res_close = fd_close(fd, UNFS3_FD_WRITE, FD_CLOSE_REAL);
427
428 /* we always do fsync(), never fdatasync() */
429 if (argp->stable == DATA_SYNC)
430 argp->stable = FILE_SYNC;
431
432 if (res != -1 && res_close != -1) {
433 result.WRITE3res_u.resok.count = res;
434 result.WRITE3res_u.resok.committed = argp->stable;
435 memcpy(result.WRITE3res_u.resok.verf, wverf,
436 NFS3_WRITEVERFSIZE);
437 } else {
438 /* error during write or close */
439 result.status = write_write_err();
440 }
441 } else
442 /* could not open for writing */
443 result.status = write_open_err();
444 }
445
446 /* overlaps with resfail */
447 result.WRITE3res_u.resok.file_wcc.before = get_pre_cached();
448 result.WRITE3res_u.resok.file_wcc.after = get_post_stat(path, rqstp);
449
450 return &result;
451 }
452
453 #ifndef WIN32
454
455 /*
456 * store verifier in atime and mtime
457 */
store_create_verifier(char * obj,createverf3 verf)458 static int store_create_verifier(char *obj, createverf3 verf)
459 {
460 struct utimbuf ubuf;
461
462 ubuf.actime = verf[0] | verf[1] << 8 | verf[2] << 16 | verf[3] << 24;
463 ubuf.modtime = verf[4] | verf[5] << 8 | verf[6] << 16 | verf[7] << 24;
464
465 return backend_utime(obj, &ubuf);
466 }
467
468 /*
469 * check if a create verifier matches
470 */
check_create_verifier(backend_statstruct * buf,createverf3 verf)471 static int check_create_verifier(backend_statstruct * buf, createverf3 verf)
472 {
473 return ((buf->st_atime ==
474 (verf[0] | verf[1] << 8 | verf[2] << 16 | verf[3] << 24))
475 && (buf->st_mtime ==
476 (verf[4] | verf[5] << 8 | verf[6] << 16 | verf[7] << 24)));
477 }
478 #endif /* WIN32 */
479
nfsproc3_create_3_svc(CREATE3args * argp,struct svc_req * rqstp)480 CREATE3res *nfsproc3_create_3_svc(CREATE3args * argp, struct svc_req * rqstp)
481 {
482 static CREATE3res result;
483 char *path;
484 char obj[NFS_MAXPATHLEN];
485 sattr3 new_attr;
486 int fd = -1, res = -1;
487 backend_statstruct buf;
488 uint32 gen;
489 int flags = O_RDWR | O_CREAT | O_TRUNC | O_NONBLOCK;
490
491 PREP(path, argp->where.dir);
492 result.status = join(cat_name(path, argp->where.name, obj), exports_rw());
493
494 cluster_create(obj, rqstp, &result.status);
495
496 /* GUARDED and EXCLUSIVE maps to Unix exclusive create */
497 if (argp->how.mode != UNCHECKED)
498 flags = flags | O_EXCL;
499
500 if (argp->how.mode != EXCLUSIVE) {
501 new_attr = argp->how.createhow3_u.obj_attributes;
502 result.status = join(result.status, atomic_attr(new_attr));
503 }
504
505 /* Try to open the file */
506 if (result.status == NFS3_OK) {
507 if (argp->how.mode != EXCLUSIVE) {
508 fd = backend_open_create(obj, flags, create_mode(new_attr));
509 } else {
510 fd = backend_open_create(obj, flags, create_mode(new_attr));
511 }
512 }
513
514 if (fd != -1) {
515 /* Successful open */
516 res = backend_fstat(fd, &buf);
517 if (res != -1) {
518 /* Successful stat */
519 if (argp->how.mode == EXCLUSIVE) {
520 /* Save verifier in atime and mtime */
521 res =
522 backend_store_create_verifier(obj,
523 argp->how.createhow3_u.
524 verf);
525 }
526 }
527
528 if (res != -1) {
529 /* So far, so good */
530 gen = backend_get_gen(buf, fd, obj);
531 fh_cache_add(buf.st_dev, buf.st_ino, obj);
532 backend_close(fd);
533
534 result.CREATE3res_u.resok.obj =
535 fh_extend_post(argp->where.dir, buf.st_dev, buf.st_ino, gen);
536 result.CREATE3res_u.resok.obj_attributes =
537 get_post_buf(buf, rqstp);
538 }
539
540 if (res == -1) {
541 /* backend_fstat() or backend_store_create_verifier() failed */
542 backend_close(fd);
543 result.status = NFS3ERR_IO;
544 }
545
546 } else if (result.status == NFS3_OK) {
547 /* open() failed */
548 if (argp->how.mode == EXCLUSIVE && errno == EEXIST) {
549 /* Check if verifier matches */
550 fd = backend_open(obj, O_NONBLOCK);
551 if (fd != -1) {
552 res = backend_fstat(fd, &buf);
553 }
554
555 if (res != -1) {
556 if (backend_check_create_verifier
557 (&buf, argp->how.createhow3_u.verf)) {
558 /* The verifier matched. Return success */
559 gen = backend_get_gen(buf, fd, obj);
560 fh_cache_add(buf.st_dev, buf.st_ino, obj);
561 backend_close(fd);
562
563 result.CREATE3res_u.resok.obj =
564 fh_extend_post(argp->where.dir, buf.st_dev,
565 buf.st_ino, gen);
566 result.CREATE3res_u.resok.obj_attributes =
567 get_post_buf(buf, rqstp);
568 } else {
569 /* The verifier doesn't match */
570 result.status = NFS3ERR_EXIST;
571 }
572 }
573 }
574 if (res == -1) {
575 result.status = create_err();
576 }
577 }
578
579 /* overlaps with resfail */
580 result.CREATE3res_u.resok.dir_wcc.before = get_pre_cached();
581 result.CREATE3res_u.resok.dir_wcc.after = get_post_stat(path, rqstp);
582
583 return &result;
584 }
585
nfsproc3_mkdir_3_svc(MKDIR3args * argp,struct svc_req * rqstp)586 MKDIR3res *nfsproc3_mkdir_3_svc(MKDIR3args * argp, struct svc_req * rqstp)
587 {
588 static MKDIR3res result;
589 char *path;
590 pre_op_attr pre;
591 post_op_attr post;
592 char obj[NFS_MAXPATHLEN];
593 int res;
594
595 PREP(path, argp->where.dir);
596 pre = get_pre_cached();
597 result.status =
598 join3(cat_name(path, argp->where.name, obj),
599 atomic_attr(argp->attributes), exports_rw());
600
601 cluster_create(obj, rqstp, &result.status);
602
603 if (result.status == NFS3_OK) {
604 res = backend_mkdir(obj, create_mode(argp->attributes));
605 if (res == -1)
606 result.status = mkdir_err();
607 else {
608 result.MKDIR3res_u.resok.obj =
609 fh_extend_type(argp->where.dir, obj, S_IFDIR);
610 result.MKDIR3res_u.resok.obj_attributes = get_post_cached(rqstp);
611 }
612 }
613
614 post = get_post_attr(path, argp->where.dir, rqstp);
615
616 /* overlaps with resfail */
617 result.MKDIR3res_u.resok.dir_wcc.before = pre;
618 result.MKDIR3res_u.resok.dir_wcc.after = post;
619
620 return &result;
621 }
622
nfsproc3_symlink_3_svc(SYMLINK3args * argp,struct svc_req * rqstp)623 SYMLINK3res *nfsproc3_symlink_3_svc(SYMLINK3args * argp,
624 struct svc_req * rqstp)
625 {
626 static SYMLINK3res result;
627 char *path;
628 pre_op_attr pre;
629 post_op_attr post;
630 char obj[NFS_MAXPATHLEN];
631 int res;
632 mode_t new_mode;
633
634 PREP(path, argp->where.dir);
635 pre = get_pre_cached();
636 result.status =
637 join3(cat_name(path, argp->where.name, obj),
638 atomic_attr(argp->symlink.symlink_attributes), exports_rw());
639
640 cluster_create(obj, rqstp, &result.status);
641
642 if (argp->symlink.symlink_attributes.mode.set_it == TRUE)
643 new_mode = create_mode(argp->symlink.symlink_attributes);
644 else {
645 /* default rwxrwxrwx */
646 new_mode =
647 S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP |
648 S_IROTH | S_IWOTH | S_IXOTH;
649 }
650
651 if (result.status == NFS3_OK) {
652 umask(~new_mode);
653 res = backend_symlink(argp->symlink.symlink_data, obj);
654 umask(0);
655 if (res == -1)
656 result.status = symlink_err();
657 else {
658 result.SYMLINK3res_u.resok.obj =
659 fh_extend_type(argp->where.dir, obj, S_IFLNK);
660 result.SYMLINK3res_u.resok.obj_attributes =
661 get_post_cached(rqstp);
662 }
663 }
664
665 post = get_post_attr(path, argp->where.dir, rqstp);
666
667 /* overlaps with resfail */
668 result.SYMLINK3res_u.resok.dir_wcc.before = pre;
669 result.SYMLINK3res_u.resok.dir_wcc.after = post;
670
671 return &result;
672 }
673
674 #ifndef WIN32
675
676 /*
677 * create Unix socket
678 */
mksocket(const char * path,mode_t mode)679 static int mksocket(const char *path, mode_t mode)
680 {
681 int res, sock;
682 struct sockaddr_un addr;
683
684 sock = socket(PF_UNIX, SOCK_STREAM, 0);
685 addr.sun_family = AF_UNIX;
686 strcpy(addr.sun_path, path);
687 res = sock;
688 if (res != -1) {
689 umask(~mode);
690 res =
691 bind(sock, (struct sockaddr *) &addr,
692 sizeof(addr.sun_family) + strlen(addr.sun_path));
693 umask(0);
694 close(sock);
695 }
696 return res;
697 }
698
699 #endif /* WIN32 */
700
701 /*
702 * check and process arguments to MKNOD procedure
703 */
mknod_args(mknoddata3 what,const char * obj,mode_t * mode,dev_t * dev)704 static nfsstat3 mknod_args(mknoddata3 what, const char *obj, mode_t * mode,
705 dev_t * dev)
706 {
707 sattr3 attr;
708
709 /* determine attributes */
710 switch (what.type) {
711 case NF3REG:
712 case NF3DIR:
713 case NF3LNK:
714 return NFS3ERR_INVAL;
715 case NF3SOCK:
716 if (strlen(obj) + 1 > UNIX_PATH_MAX)
717 return NFS3ERR_NAMETOOLONG;
718 /* fall thru */
719 case NF3FIFO:
720 attr = what.mknoddata3_u.pipe_attributes;
721 break;
722 case NF3BLK:
723 case NF3CHR:
724 attr = what.mknoddata3_u.device.dev_attributes;
725 *dev = (what.mknoddata3_u.device.spec.specdata1 << 8)
726 + what.mknoddata3_u.device.spec.specdata2;
727 break;
728 }
729
730 *mode = create_mode(attr);
731
732 /* adjust mode for creation of device special files */
733 switch (what.type) {
734 case NF3CHR:
735 *mode |= S_IFCHR;
736 break;
737 case NF3BLK:
738 *mode |= S_IFBLK;
739 break;
740 default:
741 break;
742 }
743
744 return atomic_attr(attr);
745 }
746
nfsproc3_mknod_3_svc(MKNOD3args * argp,struct svc_req * rqstp)747 MKNOD3res *nfsproc3_mknod_3_svc(MKNOD3args * argp, struct svc_req * rqstp)
748 {
749 static MKNOD3res result;
750 char *path;
751 pre_op_attr pre;
752 post_op_attr post;
753 char obj[NFS_MAXPATHLEN];
754 int res;
755 mode_t new_mode = 0;
756 dev_t dev = 0;
757
758 PREP(path, argp->where.dir);
759 pre = get_pre_cached();
760 result.status =
761 join3(cat_name(path, argp->where.name, obj),
762 mknod_args(argp->what, obj, &new_mode, &dev), exports_rw());
763
764 cluster_create(obj, rqstp, &result.status);
765
766 if (result.status == NFS3_OK) {
767 if (argp->what.type == NF3CHR || argp->what.type == NF3BLK)
768 res = backend_mknod(obj, new_mode, dev); /* device */
769 else if (argp->what.type == NF3FIFO)
770 res = backend_mkfifo(obj, new_mode); /* FIFO */
771 else
772 res = backend_mksocket(obj, new_mode); /* socket */
773
774 if (res == -1) {
775 result.status = mknod_err();
776 } else {
777 result.MKNOD3res_u.resok.obj =
778 fh_extend_type(argp->where.dir, obj,
779 type_to_mode(argp->what.type));
780 result.MKNOD3res_u.resok.obj_attributes = get_post_cached(rqstp);
781 }
782 }
783
784 post = get_post_attr(path, argp->where.dir, rqstp);
785
786 /* overlaps with resfail */
787 result.MKNOD3res_u.resok.dir_wcc.before = pre;
788 result.MKNOD3res_u.resok.dir_wcc.after = post;
789
790 return &result;
791 }
792
nfsproc3_remove_3_svc(REMOVE3args * argp,struct svc_req * rqstp)793 REMOVE3res *nfsproc3_remove_3_svc(REMOVE3args * argp, struct svc_req * rqstp)
794 {
795 static REMOVE3res result;
796 char *path;
797 char obj[NFS_MAXPATHLEN];
798 int res;
799
800 PREP(path, argp->object.dir);
801 result.status =
802 join(cat_name(path, argp->object.name, obj), exports_rw());
803
804 cluster_lookup(obj, rqstp, &result.status);
805
806 if (result.status == NFS3_OK) {
807 change_readdir_cookie();
808 res = backend_remove(obj);
809 if (res == -1)
810 result.status = remove_err();
811 }
812
813 /* overlaps with resfail */
814 result.REMOVE3res_u.resok.dir_wcc.before = get_pre_cached();
815 result.REMOVE3res_u.resok.dir_wcc.after = get_post_stat(path, rqstp);
816
817 return &result;
818 }
819
nfsproc3_rmdir_3_svc(RMDIR3args * argp,struct svc_req * rqstp)820 RMDIR3res *nfsproc3_rmdir_3_svc(RMDIR3args * argp, struct svc_req * rqstp)
821 {
822 static RMDIR3res result;
823 char *path;
824 char obj[NFS_MAXPATHLEN];
825 int res;
826
827 PREP(path, argp->object.dir);
828 result.status =
829 join(cat_name(path, argp->object.name, obj), exports_rw());
830
831 cluster_lookup(obj, rqstp, &result.status);
832
833 if (result.status == NFS3_OK) {
834 change_readdir_cookie();
835 res = backend_rmdir(obj);
836 if (res == -1)
837 result.status = rmdir_err();
838 }
839
840 /* overlaps with resfail */
841 result.RMDIR3res_u.resok.dir_wcc.before = get_pre_cached();
842 result.RMDIR3res_u.resok.dir_wcc.after = get_post_stat(path, rqstp);
843
844 return &result;
845 }
846
nfsproc3_rename_3_svc(RENAME3args * argp,struct svc_req * rqstp)847 RENAME3res *nfsproc3_rename_3_svc(RENAME3args * argp, struct svc_req * rqstp)
848 {
849 static RENAME3res result;
850 char *from;
851 char *to;
852 char from_obj[NFS_MAXPATHLEN];
853 char to_obj[NFS_MAXPATHLEN];
854 pre_op_attr pre;
855 post_op_attr post;
856 int res;
857
858 PREP(from, argp->from.dir);
859 pre = get_pre_cached();
860 result.status =
861 join(cat_name(from, argp->from.name, from_obj), exports_rw());
862
863 cluster_lookup(from_obj, rqstp, &result.status);
864
865 to = fh_decomp(argp->to.dir);
866
867 if (result.status == NFS3_OK) {
868 result.status =
869 join(cat_name(to, argp->to.name, to_obj),
870 exports_compat(to, rqstp));
871
872 cluster_create(to_obj, rqstp, &result.status);
873
874 if (result.status == NFS3_OK) {
875 change_readdir_cookie();
876 res = backend_rename(from_obj, to_obj);
877 if (res == -1)
878 result.status = rename_err();
879 }
880 }
881
882 post = get_post_attr(from, argp->from.dir, rqstp);
883
884 /* overlaps with resfail */
885 result.RENAME3res_u.resok.fromdir_wcc.before = pre;
886 result.RENAME3res_u.resok.fromdir_wcc.after = post;
887 result.RENAME3res_u.resok.todir_wcc.before = get_pre_cached();
888 result.RENAME3res_u.resok.todir_wcc.after = get_post_stat(to, rqstp);
889
890 return &result;
891 }
892
nfsproc3_link_3_svc(LINK3args * argp,struct svc_req * rqstp)893 LINK3res *nfsproc3_link_3_svc(LINK3args * argp, struct svc_req * rqstp)
894 {
895 static LINK3res result;
896 char *path, *old;
897 pre_op_attr pre;
898 post_op_attr post;
899 char obj[NFS_MAXPATHLEN];
900 int res;
901
902 PREP(path, argp->link.dir);
903 pre = get_pre_cached();
904 result.status = join(cat_name(path, argp->link.name, obj), exports_rw());
905
906 cluster_create(obj, rqstp, &result.status);
907
908 old = fh_decomp(argp->file);
909
910 if (old && result.status == NFS3_OK) {
911 result.status = exports_compat(old, rqstp);
912
913 if (result.status == NFS3_OK) {
914 res = backend_link(old, obj);
915 if (res == -1)
916 result.status = link_err();
917 }
918 } else if (!old)
919 result.status = NFS3ERR_STALE;
920
921 post = get_post_attr(path, argp->link.dir, rqstp);
922
923 /* overlaps with resfail */
924 result.LINK3res_u.resok.file_attributes = get_post_stat(old, rqstp);
925 result.LINK3res_u.resok.linkdir_wcc.before = pre;
926 result.LINK3res_u.resok.linkdir_wcc.after = post;
927
928 return &result;
929 }
930
nfsproc3_readdir_3_svc(READDIR3args * argp,struct svc_req * rqstp)931 READDIR3res *nfsproc3_readdir_3_svc(READDIR3args * argp,
932 struct svc_req * rqstp)
933 {
934 static READDIR3res result;
935 char *path;
936
937 PREP(path, argp->dir);
938
939 result = read_dir(path, argp->cookie, argp->cookieverf, argp->count);
940 result.READDIR3res_u.resok.dir_attributes = get_post_stat(path, rqstp);
941
942 return &result;
943 }
944
nfsproc3_readdirplus_3_svc(U (READDIRPLUS3args * argp),U (struct svc_req * rqstp))945 READDIRPLUS3res *nfsproc3_readdirplus_3_svc(U(READDIRPLUS3args * argp),
946 U(struct svc_req * rqstp))
947 {
948 static READDIRPLUS3res result;
949
950 /*
951 * we don't do READDIRPLUS since it involves filehandle and
952 * attribute getting which is impossible to do atomically
953 * from user-space
954 */
955 result.status = NFS3ERR_NOTSUPP;
956 result.READDIRPLUS3res_u.resfail.dir_attributes.attributes_follow = FALSE;
957
958 return &result;
959 }
960
nfsproc3_fsstat_3_svc(FSSTAT3args * argp,struct svc_req * rqstp)961 FSSTAT3res *nfsproc3_fsstat_3_svc(FSSTAT3args * argp, struct svc_req * rqstp)
962 {
963 static FSSTAT3res result;
964 char *path;
965 backend_statvfsstruct buf;
966 int res;
967
968 PREP(path, argp->fsroot);
969
970 /* overlaps with resfail */
971 result.FSSTAT3res_u.resok.obj_attributes = get_post_cached(rqstp);
972
973 res = backend_statvfs(path, &buf);
974 if (res == -1) {
975 /* statvfs fell on its nose */
976 if ((exports_opts & OPT_REMOVABLE) && export_point(path)) {
977 /* Removable media export point; probably no media inserted.
978 Return dummy values. */
979 result.status = NFS3_OK;
980 result.FSSTAT3res_u.resok.tbytes = 0;
981 result.FSSTAT3res_u.resok.fbytes = 0;
982 result.FSSTAT3res_u.resok.abytes = 0;
983 result.FSSTAT3res_u.resok.tfiles = 0;
984 result.FSSTAT3res_u.resok.ffiles = 0;
985 result.FSSTAT3res_u.resok.afiles = 0;
986 result.FSSTAT3res_u.resok.invarsec = 0;
987 } else {
988 result.status = NFS3ERR_IO;
989 }
990 } else {
991 result.status = NFS3_OK;
992 result.FSSTAT3res_u.resok.tbytes =
993 (uint64) buf.f_blocks * buf.f_frsize;
994 result.FSSTAT3res_u.resok.fbytes =
995 (uint64) buf.f_bfree * buf.f_frsize;
996 result.FSSTAT3res_u.resok.abytes =
997 (uint64) buf.f_bavail * buf.f_frsize;
998 result.FSSTAT3res_u.resok.tfiles = buf.f_files;
999 result.FSSTAT3res_u.resok.ffiles = buf.f_ffree;
1000 result.FSSTAT3res_u.resok.afiles = buf.f_ffree;
1001 result.FSSTAT3res_u.resok.invarsec = 0;
1002 }
1003
1004 return &result;
1005 }
1006
nfsproc3_fsinfo_3_svc(FSINFO3args * argp,struct svc_req * rqstp)1007 FSINFO3res *nfsproc3_fsinfo_3_svc(FSINFO3args * argp, struct svc_req * rqstp)
1008 {
1009 static FSINFO3res result;
1010 char *path;
1011 unsigned int maxdata;
1012
1013 if (get_socket_type(rqstp) == SOCK_STREAM)
1014 maxdata = NFS_MAXDATA_TCP;
1015 else
1016 maxdata = NFS_MAXDATA_UDP;
1017
1018 PREP(path, argp->fsroot);
1019
1020 result.FSINFO3res_u.resok.obj_attributes = get_post_cached(rqstp);
1021
1022 result.status = NFS3_OK;
1023 result.FSINFO3res_u.resok.rtmax = maxdata;
1024 result.FSINFO3res_u.resok.rtpref = maxdata;
1025 result.FSINFO3res_u.resok.rtmult = 4096;
1026 result.FSINFO3res_u.resok.wtmax = maxdata;
1027 result.FSINFO3res_u.resok.wtpref = maxdata;
1028 result.FSINFO3res_u.resok.wtmult = 4096;
1029 result.FSINFO3res_u.resok.dtpref = 4096;
1030 result.FSINFO3res_u.resok.maxfilesize = ~0ULL;
1031 result.FSINFO3res_u.resok.time_delta.seconds = backend_time_delta_seconds;
1032 result.FSINFO3res_u.resok.time_delta.nseconds = 0;
1033 result.FSINFO3res_u.resok.properties = backend_fsinfo_properties;
1034
1035 return &result;
1036 }
1037
nfsproc3_pathconf_3_svc(PATHCONF3args * argp,struct svc_req * rqstp)1038 PATHCONF3res *nfsproc3_pathconf_3_svc(PATHCONF3args * argp,
1039 struct svc_req * rqstp)
1040 {
1041 static PATHCONF3res result;
1042 char *path;
1043
1044 PREP(path, argp->object);
1045
1046 result.PATHCONF3res_u.resok.obj_attributes = get_post_cached(rqstp);
1047
1048 result.status = NFS3_OK;
1049 result.PATHCONF3res_u.resok.linkmax = 0xFFFFFFFF;
1050 result.PATHCONF3res_u.resok.name_max = NFS_MAXPATHLEN;
1051 result.PATHCONF3res_u.resok.no_trunc = TRUE;
1052 result.PATHCONF3res_u.resok.chown_restricted = FALSE;
1053 result.PATHCONF3res_u.resok.case_insensitive =
1054 backend_pathconf_case_insensitive;
1055 result.PATHCONF3res_u.resok.case_preserving = TRUE;
1056
1057 return &result;
1058 }
1059
nfsproc3_commit_3_svc(COMMIT3args * argp,struct svc_req * rqstp)1060 COMMIT3res *nfsproc3_commit_3_svc(COMMIT3args * argp, struct svc_req * rqstp)
1061 {
1062 static COMMIT3res result;
1063 char *path;
1064 int res;
1065
1066 PREP(path, argp->file);
1067 result.status = join(is_reg(), exports_rw());
1068
1069 if (result.status == NFS3_OK) {
1070 res = fd_sync(argp->file);
1071 if (res != -1)
1072 memcpy(result.COMMIT3res_u.resok.verf, wverf, NFS3_WRITEVERFSIZE);
1073 else
1074 /* error during fsync() or close() */
1075 result.status = NFS3ERR_IO;
1076 }
1077
1078 /* overlaps with resfail */
1079 result.COMMIT3res_u.resfail.file_wcc.before = get_pre_cached();
1080 result.COMMIT3res_u.resfail.file_wcc.after = get_post_stat(path, rqstp);
1081
1082 return &result;
1083 }
1084