Added initial unfs3 sources for version 0.9.22+dfsg-1maemo2
[unfs3] / unfs3 / nfs.c
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  */
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
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
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  */
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
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
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
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
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
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
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  */
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  */
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
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
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
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  */
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  */
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
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
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
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
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
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
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
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
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
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
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
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 }