3 * UNFS3 NFS protocol procedures
4 * (C) 2004, Pascal Schmidt
5 * see file LICENSE for license details
10 #include <sys/types.h>
12 #include <sys/param.h> /* needed for statfs() on NetBSD */
13 #if HAVE_SYS_MOUNT_H == 1
14 #include <sys/mount.h> /* dito */
16 #if HAVE_SYS_VMOUNT_H == 1
17 #include <sys/vmount.h> /* AIX */
28 #include <sys/socket.h>
33 # include <sys/statvfs.h>
35 # define statvfs statfs
49 #include "Config/exports.h"
50 #include "Extras/cluster.h"
53 * decompose filehandle and switch user if permitted access
54 * otherwise zero result structure and return with error status
56 #define PREP(p,f) do { \
57 unfs3_fh_t *fh = (void *)f.data.data_val; \
60 if (exports_options(p, rqstp, NULL, NULL) == -1) { \
61 memset(&result, 0, sizeof(result)); \
63 result.status = NFS3ERR_ACCES; \
65 result.status = NFS3ERR_STALE; \
68 if (fh->pwhash != export_password_hash) { \
69 memset(&result, 0, sizeof(result)); \
70 result.status = NFS3ERR_STALE; \
77 * cat an object name onto a path, checking for illegal input
79 nfsstat3 cat_name(const char *path, const char *name, char *result)
89 if (name[0] == 0 || strchr(name, '/') != NULL)
92 if (strlen(path) + strlen(name) + 2 > NFS_MAXPATHLEN)
93 return NFS3ERR_NAMETOOLONG;
95 if (strcmp(name, ".") == 0) {
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.
107 if (strcmp(name, "..") == 0) {
108 last = strrchr(path, '/');
109 if (!last || last == path)
113 strcpy(result, path);
119 sprintf(result, "%s/%s", path, name);
123 void *nfsproc3_null_3_svc(U(void *argp), U(struct svc_req *rqstp))
125 static void *result = NULL;
130 GETATTR3res *nfsproc3_getattr_3_svc(GETATTR3args * argp,
131 struct svc_req * rqstp)
133 static GETATTR3res result;
137 PREP(path, argp->object);
138 post = get_post_cached(rqstp);
140 result.status = NFS3_OK;
141 result.GETATTR3res_u.resok.obj_attributes =
142 post.post_op_attr_u.attributes;
148 * check ctime guard for SETATTR procedure
150 static nfsstat3 in_sync(sattrguard3 guard, pre_op_attr pre)
152 if (!pre.attributes_follow)
153 return NFS3ERR_STALE;
158 if (guard.sattrguard3_u.obj_ctime.seconds !=
159 pre.pre_op_attr_u.attributes.ctime.seconds)
160 return NFS3ERR_NOT_SYNC;
165 SETATTR3res *nfsproc3_setattr_3_svc(SETATTR3args * argp,
166 struct svc_req * rqstp)
168 static SETATTR3res result;
172 PREP(path, argp->object);
173 pre = get_pre_cached();
174 result.status = join(in_sync(argp->guard, pre), exports_rw());
176 if (result.status == NFS3_OK)
177 result.status = set_attr(path, argp->object, argp->new_attributes);
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);
186 LOOKUP3res *nfsproc3_lookup_3_svc(LOOKUP3args * argp, struct svc_req * rqstp)
188 static LOOKUP3res result;
191 char obj[NFS_MAXPATHLEN];
192 backend_statstruct buf;
196 PREP(path, argp->what.dir);
197 result.status = cat_name(path, argp->what.name, obj);
199 cluster_lookup(obj, rqstp, &result.status);
201 if (result.status == NFS3_OK) {
202 res = backend_lstat(obj, &buf);
204 result.status = lookup_err();
206 if (strcmp(argp->what.name, ".") == 0 ||
207 strcmp(argp->what.name, "..") == 0) {
208 fh = fh_comp_ptr(obj, rqstp, 0);
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);
216 result.LOOKUP3res_u.resok.object.data.data_len =
218 result.LOOKUP3res_u.resok.object.data.data_val = (char *) fh;
219 result.LOOKUP3res_u.resok.obj_attributes =
220 get_post_buf(buf, rqstp);
222 /* path was too long */
223 result.status = NFS3ERR_NAMETOOLONG;
228 /* overlaps with resfail */
229 result.LOOKUP3res_u.resok.dir_attributes = get_post_stat(path, rqstp);
234 ACCESS3res *nfsproc3_access_3_svc(ACCESS3args * argp, struct svc_req * rqstp)
236 static ACCESS3res result;
242 PREP(path, argp->object);
243 post = get_post_cached(rqstp);
244 mode = post.post_op_attr_u.attributes.mode;
246 /* owner permissions */
247 if (is_owner(st_cache.st_uid, rqstp)) {
249 access |= ACCESS3_READ;
251 access |= ACCESS3_MODIFY | ACCESS3_EXTEND;
252 if (mode & S_IXUSR) {
253 access |= ACCESS3_EXECUTE;
254 if (opt_readable_executables)
255 access |= ACCESS3_READ;
257 } else if (has_group(st_cache.st_gid, rqstp)) {
258 /* group permissions */
260 access |= ACCESS3_READ;
262 access |= ACCESS3_MODIFY | ACCESS3_EXTEND;
263 if (mode & S_IXGRP) {
264 access |= ACCESS3_EXECUTE;
265 if (opt_readable_executables)
266 access |= ACCESS3_READ;
269 /* other permissions */
271 access |= ACCESS3_READ;
273 access |= ACCESS3_MODIFY | ACCESS3_EXTEND;
274 if (mode & S_IXOTH) {
275 access |= ACCESS3_EXECUTE;
276 if (opt_readable_executables)
277 access |= ACCESS3_READ;
281 /* root is allowed everything */
282 if (get_uid(rqstp) == 0)
283 access |= ACCESS3_READ | ACCESS3_MODIFY | ACCESS3_EXTEND;
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;
294 result.status = NFS3_OK;
295 result.ACCESS3res_u.resok.access = access & argp->access;
296 result.ACCESS3res_u.resok.obj_attributes = post;
301 READLINK3res *nfsproc3_readlink_3_svc(READLINK3args * argp,
302 struct svc_req * rqstp)
304 static READLINK3res result;
306 static char buf[NFS_MAXPATHLEN];
309 PREP(path, argp->symlink);
311 res = backend_readlink(path, buf, NFS_MAXPATHLEN - 1);
313 result.status = readlink_err();
315 /* readlink does not NULL-terminate */
318 result.status = NFS3_OK;
319 result.READLINK3res_u.resok.data = buf;
322 /* overlaps with resfail */
323 result.READLINK3res_u.resok.symlink_attributes =
324 get_post_stat(path, rqstp);
329 READ3res *nfsproc3_read_3_svc(READ3args * argp, struct svc_req * rqstp)
331 static READ3res result;
334 static char buf[NFS_MAXDATA_TCP + 1];
335 unsigned int maxdata;
337 if (get_socket_type(rqstp) == SOCK_STREAM)
338 maxdata = NFS_MAXDATA_TCP;
340 maxdata = NFS_MAXDATA_UDP;
342 PREP(path, argp->file);
343 result.status = is_reg();
345 /* handle reading of executables */
346 read_executable(rqstp, st_cache);
348 /* handle read of owned files */
349 read_by_owner(rqstp, st_cache);
351 /* if bigger than rtmax, truncate length */
352 if (argp->count > maxdata)
353 argp->count = maxdata;
355 if (result.status == NFS3_OK) {
356 fd = fd_open(path, argp->file, UNFS3_FD_READ, TRUE);
358 /* read one more to check for eof */
359 res = backend_pread(fd, buf, argp->count + 1, argp->offset);
361 /* eof if we could not read one more */
362 result.READ3res_u.resok.eof = (res <= (int64) argp->count);
364 /* close for real when hitting eof */
365 if (result.READ3res_u.resok.eof)
366 fd_close(fd, UNFS3_FD_READ, FD_CLOSE_REAL);
368 fd_close(fd, UNFS3_FD_READ, FD_CLOSE_VIRT);
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;
377 /* error during read() */
379 /* EINVAL means unreadable object */
381 result.status = NFS3ERR_INVAL;
383 result.status = NFS3ERR_IO;
386 /* opening for read failed */
387 result.status = read_err();
390 /* overlaps with resfail */
391 result.READ3res_u.resok.file_attributes = get_post_stat(path, rqstp);
396 WRITE3res *nfsproc3_write_3_svc(WRITE3args * argp, struct svc_req * rqstp)
398 static WRITE3res result;
400 int fd, res, res_close;
402 PREP(path, argp->file);
403 result.status = join(is_reg(), exports_rw());
405 /* handle write of owned files */
406 write_by_owner(rqstp, st_cache);
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
415 fd = fd_open(path, argp->file, UNFS3_FD_WRITE,
416 (argp->stable == UNSTABLE));
419 backend_pwrite(fd, argp->data.data_val, argp->data.data_len,
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);
426 res_close = fd_close(fd, UNFS3_FD_WRITE, FD_CLOSE_REAL);
428 /* we always do fsync(), never fdatasync() */
429 if (argp->stable == DATA_SYNC)
430 argp->stable = FILE_SYNC;
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,
438 /* error during write or close */
439 result.status = write_write_err();
442 /* could not open for writing */
443 result.status = write_open_err();
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);
456 * store verifier in atime and mtime
458 static int store_create_verifier(char *obj, createverf3 verf)
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;
465 return backend_utime(obj, &ubuf);
469 * check if a create verifier matches
471 static int check_create_verifier(backend_statstruct * buf, createverf3 verf)
473 return ((buf->st_atime ==
474 (verf[0] | verf[1] << 8 | verf[2] << 16 | verf[3] << 24))
476 (verf[4] | verf[5] << 8 | verf[6] << 16 | verf[7] << 24)));
480 CREATE3res *nfsproc3_create_3_svc(CREATE3args * argp, struct svc_req * rqstp)
482 static CREATE3res result;
484 char obj[NFS_MAXPATHLEN];
486 int fd = -1, res = -1;
487 backend_statstruct buf;
489 int flags = O_RDWR | O_CREAT | O_TRUNC | O_NONBLOCK;
491 PREP(path, argp->where.dir);
492 result.status = join(cat_name(path, argp->where.name, obj), exports_rw());
494 cluster_create(obj, rqstp, &result.status);
496 /* GUARDED and EXCLUSIVE maps to Unix exclusive create */
497 if (argp->how.mode != UNCHECKED)
498 flags = flags | O_EXCL;
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));
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));
510 fd = backend_open_create(obj, flags, create_mode(new_attr));
515 /* Successful open */
516 res = backend_fstat(fd, &buf);
518 /* Successful stat */
519 if (argp->how.mode == EXCLUSIVE) {
520 /* Save verifier in atime and mtime */
522 backend_store_create_verifier(obj,
523 argp->how.createhow3_u.
529 /* So far, so good */
530 gen = backend_get_gen(buf, fd, obj);
531 fh_cache_add(buf.st_dev, buf.st_ino, obj);
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);
541 /* backend_fstat() or backend_store_create_verifier() failed */
543 result.status = NFS3ERR_IO;
546 } else if (result.status == NFS3_OK) {
548 if (argp->how.mode == EXCLUSIVE && errno == EEXIST) {
549 /* Check if verifier matches */
550 fd = backend_open(obj, O_NONBLOCK);
552 res = backend_fstat(fd, &buf);
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);
563 result.CREATE3res_u.resok.obj =
564 fh_extend_post(argp->where.dir, buf.st_dev,
566 result.CREATE3res_u.resok.obj_attributes =
567 get_post_buf(buf, rqstp);
569 /* The verifier doesn't match */
570 result.status = NFS3ERR_EXIST;
575 result.status = create_err();
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);
586 MKDIR3res *nfsproc3_mkdir_3_svc(MKDIR3args * argp, struct svc_req * rqstp)
588 static MKDIR3res result;
592 char obj[NFS_MAXPATHLEN];
595 PREP(path, argp->where.dir);
596 pre = get_pre_cached();
598 join3(cat_name(path, argp->where.name, obj),
599 atomic_attr(argp->attributes), exports_rw());
601 cluster_create(obj, rqstp, &result.status);
603 if (result.status == NFS3_OK) {
604 res = backend_mkdir(obj, create_mode(argp->attributes));
606 result.status = mkdir_err();
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);
614 post = get_post_attr(path, argp->where.dir, rqstp);
616 /* overlaps with resfail */
617 result.MKDIR3res_u.resok.dir_wcc.before = pre;
618 result.MKDIR3res_u.resok.dir_wcc.after = post;
623 SYMLINK3res *nfsproc3_symlink_3_svc(SYMLINK3args * argp,
624 struct svc_req * rqstp)
626 static SYMLINK3res result;
630 char obj[NFS_MAXPATHLEN];
634 PREP(path, argp->where.dir);
635 pre = get_pre_cached();
637 join3(cat_name(path, argp->where.name, obj),
638 atomic_attr(argp->symlink.symlink_attributes), exports_rw());
640 cluster_create(obj, rqstp, &result.status);
642 if (argp->symlink.symlink_attributes.mode.set_it == TRUE)
643 new_mode = create_mode(argp->symlink.symlink_attributes);
645 /* default rwxrwxrwx */
647 S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP |
648 S_IROTH | S_IWOTH | S_IXOTH;
651 if (result.status == NFS3_OK) {
653 res = backend_symlink(argp->symlink.symlink_data, obj);
656 result.status = symlink_err();
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);
665 post = get_post_attr(path, argp->where.dir, rqstp);
667 /* overlaps with resfail */
668 result.SYMLINK3res_u.resok.dir_wcc.before = pre;
669 result.SYMLINK3res_u.resok.dir_wcc.after = post;
679 static int mksocket(const char *path, mode_t mode)
682 struct sockaddr_un addr;
684 sock = socket(PF_UNIX, SOCK_STREAM, 0);
685 addr.sun_family = AF_UNIX;
686 strcpy(addr.sun_path, path);
691 bind(sock, (struct sockaddr *) &addr,
692 sizeof(addr.sun_family) + strlen(addr.sun_path));
702 * check and process arguments to MKNOD procedure
704 static nfsstat3 mknod_args(mknoddata3 what, const char *obj, mode_t * mode,
709 /* determine attributes */
714 return NFS3ERR_INVAL;
716 if (strlen(obj) + 1 > UNIX_PATH_MAX)
717 return NFS3ERR_NAMETOOLONG;
720 attr = what.mknoddata3_u.pipe_attributes;
724 attr = what.mknoddata3_u.device.dev_attributes;
725 *dev = (what.mknoddata3_u.device.spec.specdata1 << 8)
726 + what.mknoddata3_u.device.spec.specdata2;
730 *mode = create_mode(attr);
732 /* adjust mode for creation of device special files */
744 return atomic_attr(attr);
747 MKNOD3res *nfsproc3_mknod_3_svc(MKNOD3args * argp, struct svc_req * rqstp)
749 static MKNOD3res result;
753 char obj[NFS_MAXPATHLEN];
758 PREP(path, argp->where.dir);
759 pre = get_pre_cached();
761 join3(cat_name(path, argp->where.name, obj),
762 mknod_args(argp->what, obj, &new_mode, &dev), exports_rw());
764 cluster_create(obj, rqstp, &result.status);
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 */
772 res = backend_mksocket(obj, new_mode); /* socket */
775 result.status = mknod_err();
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);
784 post = get_post_attr(path, argp->where.dir, rqstp);
786 /* overlaps with resfail */
787 result.MKNOD3res_u.resok.dir_wcc.before = pre;
788 result.MKNOD3res_u.resok.dir_wcc.after = post;
793 REMOVE3res *nfsproc3_remove_3_svc(REMOVE3args * argp, struct svc_req * rqstp)
795 static REMOVE3res result;
797 char obj[NFS_MAXPATHLEN];
800 PREP(path, argp->object.dir);
802 join(cat_name(path, argp->object.name, obj), exports_rw());
804 cluster_lookup(obj, rqstp, &result.status);
806 if (result.status == NFS3_OK) {
807 change_readdir_cookie();
808 res = backend_remove(obj);
810 result.status = remove_err();
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);
820 RMDIR3res *nfsproc3_rmdir_3_svc(RMDIR3args * argp, struct svc_req * rqstp)
822 static RMDIR3res result;
824 char obj[NFS_MAXPATHLEN];
827 PREP(path, argp->object.dir);
829 join(cat_name(path, argp->object.name, obj), exports_rw());
831 cluster_lookup(obj, rqstp, &result.status);
833 if (result.status == NFS3_OK) {
834 change_readdir_cookie();
835 res = backend_rmdir(obj);
837 result.status = rmdir_err();
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);
847 RENAME3res *nfsproc3_rename_3_svc(RENAME3args * argp, struct svc_req * rqstp)
849 static RENAME3res result;
852 char from_obj[NFS_MAXPATHLEN];
853 char to_obj[NFS_MAXPATHLEN];
858 PREP(from, argp->from.dir);
859 pre = get_pre_cached();
861 join(cat_name(from, argp->from.name, from_obj), exports_rw());
863 cluster_lookup(from_obj, rqstp, &result.status);
865 to = fh_decomp(argp->to.dir);
867 if (result.status == NFS3_OK) {
869 join(cat_name(to, argp->to.name, to_obj),
870 exports_compat(to, rqstp));
872 cluster_create(to_obj, rqstp, &result.status);
874 if (result.status == NFS3_OK) {
875 change_readdir_cookie();
876 res = backend_rename(from_obj, to_obj);
878 result.status = rename_err();
882 post = get_post_attr(from, argp->from.dir, rqstp);
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);
893 LINK3res *nfsproc3_link_3_svc(LINK3args * argp, struct svc_req * rqstp)
895 static LINK3res result;
899 char obj[NFS_MAXPATHLEN];
902 PREP(path, argp->link.dir);
903 pre = get_pre_cached();
904 result.status = join(cat_name(path, argp->link.name, obj), exports_rw());
906 cluster_create(obj, rqstp, &result.status);
908 old = fh_decomp(argp->file);
910 if (old && result.status == NFS3_OK) {
911 result.status = exports_compat(old, rqstp);
913 if (result.status == NFS3_OK) {
914 res = backend_link(old, obj);
916 result.status = link_err();
919 result.status = NFS3ERR_STALE;
921 post = get_post_attr(path, argp->link.dir, rqstp);
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;
931 READDIR3res *nfsproc3_readdir_3_svc(READDIR3args * argp,
932 struct svc_req * rqstp)
934 static READDIR3res result;
937 PREP(path, argp->dir);
939 result = read_dir(path, argp->cookie, argp->cookieverf, argp->count);
940 result.READDIR3res_u.resok.dir_attributes = get_post_stat(path, rqstp);
945 READDIRPLUS3res *nfsproc3_readdirplus_3_svc(U(READDIRPLUS3args * argp),
946 U(struct svc_req * rqstp))
948 static READDIRPLUS3res result;
951 * we don't do READDIRPLUS since it involves filehandle and
952 * attribute getting which is impossible to do atomically
955 result.status = NFS3ERR_NOTSUPP;
956 result.READDIRPLUS3res_u.resfail.dir_attributes.attributes_follow = FALSE;
961 FSSTAT3res *nfsproc3_fsstat_3_svc(FSSTAT3args * argp, struct svc_req * rqstp)
963 static FSSTAT3res result;
965 backend_statvfsstruct buf;
968 PREP(path, argp->fsroot);
970 /* overlaps with resfail */
971 result.FSSTAT3res_u.resok.obj_attributes = get_post_cached(rqstp);
973 res = backend_statvfs(path, &buf);
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;
988 result.status = NFS3ERR_IO;
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;
1007 FSINFO3res *nfsproc3_fsinfo_3_svc(FSINFO3args * argp, struct svc_req * rqstp)
1009 static FSINFO3res result;
1011 unsigned int maxdata;
1013 if (get_socket_type(rqstp) == SOCK_STREAM)
1014 maxdata = NFS_MAXDATA_TCP;
1016 maxdata = NFS_MAXDATA_UDP;
1018 PREP(path, argp->fsroot);
1020 result.FSINFO3res_u.resok.obj_attributes = get_post_cached(rqstp);
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;
1038 PATHCONF3res *nfsproc3_pathconf_3_svc(PATHCONF3args * argp,
1039 struct svc_req * rqstp)
1041 static PATHCONF3res result;
1044 PREP(path, argp->object);
1046 result.PATHCONF3res_u.resok.obj_attributes = get_post_cached(rqstp);
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;
1060 COMMIT3res *nfsproc3_commit_3_svc(COMMIT3args * argp, struct svc_req * rqstp)
1062 static COMMIT3res result;
1066 PREP(path, argp->file);
1067 result.status = join(is_reg(), exports_rw());
1069 if (result.status == NFS3_OK) {
1070 res = fd_sync(argp->file);
1072 memcpy(result.COMMIT3res_u.resok.verf, wverf, NFS3_WRITEVERFSIZE);
1074 /* error during fsync() or close() */
1075 result.status = NFS3ERR_IO;
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);