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