Version increased to unfs3_0.9.22+dfsg-1maemo3
[unfs3] / unfs3 / attr.c
1
2 /*
3  * UNFS3 attribute handling
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 <rpc/rpc.h>
13 #include <fcntl.h>
14 #include <time.h>
15 #ifndef WIN32
16 #include <unistd.h>
17 #endif
18 #include <utime.h>
19 #include <errno.h>
20
21 #include "backend.h"
22 #include "nfs.h"
23 #include "attr.h"
24 #include "error.h"
25 #include "fh.h"
26 #include "fh_cache.h"
27 #include "daemon.h"
28 #include "user.h"
29 #include "Config/exports.h"
30
31 /*
32  * check whether stat_cache is for a regular file
33  *
34  * fh_decomp must be called before to fill the stat cache
35  */
36 nfsstat3 is_reg(void)
37 {
38     if (!st_cache_valid)
39         return NFS3ERR_STALE;
40     else if (S_ISREG(st_cache.st_mode))
41         return NFS3_OK;
42     else
43         return NFS3ERR_INVAL;
44 }
45
46 /*
47  * find stat bit corresponding to given NFS file type
48  */
49 mode_t type_to_mode(ftype3 ftype)
50 {
51     switch (ftype) {
52         case NF3REG:
53             return S_IFREG;
54         case NF3DIR:
55             return S_IFDIR;
56         case NF3LNK:
57             return S_IFLNK;
58         case NF3CHR:
59             return S_IFCHR;
60         case NF3BLK:
61             return S_IFBLK;
62         case NF3FIFO:
63             return S_IFIFO;
64         case NF3SOCK:
65             return S_IFSOCK;
66     }
67
68     /* fix gcc warning */
69     return 0;
70 }
71
72 /*
73  * post_op_attr for error returns
74  */
75 #ifdef __GNUC__
76 static post_op_attr error_attr = {.attributes_follow = FALSE };
77 #else
78 static post_op_attr error_attr = { FALSE };
79 #endif
80
81 /*
82  * return pre-operation attributes
83  *
84  * fh_decomp must be called before to fill the stat cache
85  */
86 pre_op_attr get_pre_cached(void)
87 {
88     pre_op_attr result;
89
90     if (!st_cache_valid) {
91         result.attributes_follow = FALSE;
92         return result;
93     }
94
95     result.attributes_follow = TRUE;
96
97     result.pre_op_attr_u.attributes.size = st_cache.st_size;
98     result.pre_op_attr_u.attributes.mtime.seconds = st_cache.st_mtime;
99     result.pre_op_attr_u.attributes.mtime.nseconds = 0;
100     result.pre_op_attr_u.attributes.ctime.seconds = st_cache.st_ctime;
101     result.pre_op_attr_u.attributes.ctime.nseconds = 0;
102
103     return result;
104 }
105
106 /*
107  * compute post-operation attributes given a stat buffer
108  */
109 post_op_attr get_post_buf(backend_statstruct buf, struct svc_req * req)
110 {
111     post_op_attr result;
112
113     result.attributes_follow = TRUE;
114
115     if (S_ISDIR(buf.st_mode))
116         result.post_op_attr_u.attributes.type = NF3DIR;
117     else if (S_ISBLK(buf.st_mode))
118         result.post_op_attr_u.attributes.type = NF3BLK;
119     else if (S_ISCHR(buf.st_mode))
120         result.post_op_attr_u.attributes.type = NF3CHR;
121 #ifdef S_ISLNK
122     else if (S_ISLNK(buf.st_mode))
123         result.post_op_attr_u.attributes.type = NF3LNK;
124 #endif                                 /* S_ISLNK */
125 #ifdef S_ISSOCK
126     else if (S_ISSOCK(buf.st_mode))
127         result.post_op_attr_u.attributes.type = NF3SOCK;
128 #endif                                 /* S_ISSOCK */
129     else if (S_ISFIFO(buf.st_mode))
130         result.post_op_attr_u.attributes.type = NF3FIFO;
131     else
132         result.post_op_attr_u.attributes.type = NF3REG;
133
134     /* adapt permissions for executable files */
135     if (opt_readable_executables && S_ISREG(buf.st_mode)) {
136         if (buf.st_mode & S_IXUSR)
137             buf.st_mode |= S_IRUSR;
138         if (buf.st_mode & S_IXGRP)
139             buf.st_mode |= S_IRGRP;
140         if (buf.st_mode & S_IXOTH)
141             buf.st_mode |= S_IROTH;
142     }
143
144     result.post_op_attr_u.attributes.mode = buf.st_mode & 0xFFFF;
145     result.post_op_attr_u.attributes.nlink = buf.st_nlink;
146
147     /* If -s, translate uids */
148     if (opt_singleuser) {
149         unsigned int req_uid = 0;
150         unsigned int req_gid = 0;
151         struct authunix_parms *auth = (void *) req->rq_clntcred;
152         uid_t ruid = backend_getuid();
153
154         if (req->rq_cred.oa_flavor == AUTH_UNIX) {
155             req_uid = auth->aup_uid;
156             req_gid = auth->aup_gid;
157         }
158
159         if ((buf.st_uid == ruid) || (ruid == 0))
160             result.post_op_attr_u.attributes.uid = req_uid;
161         else
162             result.post_op_attr_u.attributes.uid = 0;
163
164         if ((buf.st_gid == backend_getgid()) || (ruid == 0))
165             result.post_op_attr_u.attributes.gid = req_gid;
166         else
167             result.post_op_attr_u.attributes.gid = 0;
168     } else {
169         /* Normal case */
170         result.post_op_attr_u.attributes.uid = buf.st_uid;
171         result.post_op_attr_u.attributes.gid = buf.st_gid;
172     }
173
174     result.post_op_attr_u.attributes.size = buf.st_size;
175     result.post_op_attr_u.attributes.used = buf.st_blocks * 512;
176     result.post_op_attr_u.attributes.rdev.specdata1 =
177         (buf.st_rdev >> 8) & 0xFF;
178     result.post_op_attr_u.attributes.rdev.specdata2 = buf.st_rdev & 0xFF;
179     result.post_op_attr_u.attributes.fsid = buf.st_dev;
180
181     /* If this is a removable export point, we should return the preset fsid
182        for all objects which resides in the same file system as the exported
183        directory */
184     if (exports_opts & OPT_REMOVABLE) {
185         backend_statstruct epbuf;
186
187         if (backend_lstat(export_path, &epbuf) != -1 &&
188             buf.st_dev == epbuf.st_dev) {
189             result.post_op_attr_u.attributes.fsid = export_fsid;
190         }
191     }
192 #ifdef WIN32
193     /* Recent Linux kernels (2.6.24 and newer) exposes large fileids even to
194        non-LFS 32-bit applications, unless kernel parameter
195        nfs.enable_ino64=0. This means that applications will fail with
196        EOVERFLOW. On Windows, we always have large st_ino:s. To avoid
197        trouble, we truncate to 32 bits */
198     result.post_op_attr_u.attributes.fileid =
199         (buf.st_ino >> 32) ^ (buf.st_ino & 0xffffffff);
200 #else
201     result.post_op_attr_u.attributes.fileid = buf.st_ino;
202 #endif
203     result.post_op_attr_u.attributes.atime.seconds = buf.st_atime;
204     result.post_op_attr_u.attributes.atime.nseconds = 0;
205     result.post_op_attr_u.attributes.mtime.seconds = buf.st_mtime;
206     result.post_op_attr_u.attributes.mtime.nseconds = 0;
207     result.post_op_attr_u.attributes.ctime.seconds = buf.st_ctime;
208     result.post_op_attr_u.attributes.ctime.nseconds = 0;
209
210     return result;
211 }
212
213 /*
214  * lowlevel routine for getting post-operation attributes
215  */
216 static post_op_attr get_post_ll(const char *path, uint32 dev, uint64 ino,
217                                 struct svc_req *req)
218 {
219     backend_statstruct buf;
220     int res;
221
222     if (!path)
223         return error_attr;
224
225     res = backend_lstat(path, &buf);
226     if (res == -1)
227         return error_attr;
228
229     /* protect against local fs race */
230     if (dev != buf.st_dev || ino != buf.st_ino)
231         return error_attr;
232
233     return get_post_buf(buf, req);
234 }
235
236 /*
237  * return post-operation attributes, using fh for old dev/ino
238  */
239 post_op_attr get_post_attr(const char *path, nfs_fh3 nfh,
240                            struct svc_req * req)
241 {
242     unfs3_fh_t *fh = (void *) nfh.data.data_val;
243
244     return get_post_ll(path, fh->dev, fh->ino, req);
245 }
246
247 /*
248  * return post-operation attributes, using stat cache for old dev/ino
249  */
250 post_op_attr get_post_stat(const char *path, struct svc_req * req)
251 {
252     return get_post_ll(path, st_cache.st_dev, st_cache.st_ino, req);
253 }
254
255 /*
256  * return post-operation attributes using stat cache
257  *
258  * fd_decomp must be called before to fill the stat cache
259  */
260 post_op_attr get_post_cached(struct svc_req * req)
261 {
262     if (!st_cache_valid)
263         return error_attr;
264
265     return get_post_buf(st_cache, req);
266 }
267
268 /*
269  * setting of time, races with local filesystem
270  *
271  * there is no futimes() function in POSIX or Linux
272  */
273 static nfsstat3 set_time(const char *path, backend_statstruct buf, sattr3 new)
274 {
275     time_t new_atime, new_mtime;
276     struct utimbuf utim;
277     int res;
278
279     /* set atime and mtime */
280     if (new.atime.set_it != DONT_CHANGE || new.mtime.set_it != DONT_CHANGE) {
281
282         /* compute atime to set */
283         if (new.atime.set_it == SET_TO_SERVER_TIME)
284             new_atime = time(NULL);
285         else if (new.atime.set_it == SET_TO_CLIENT_TIME)
286             new_atime = new.atime.set_atime_u.atime.seconds;
287         else                           /* DONT_CHANGE */
288             new_atime = buf.st_atime;
289
290         /* compute mtime to set */
291         if (new.mtime.set_it == SET_TO_SERVER_TIME)
292             new_mtime = time(NULL);
293         else if (new.mtime.set_it == SET_TO_CLIENT_TIME)
294             new_mtime = new.mtime.set_mtime_u.mtime.seconds;
295         else                           /* DONT_CHANGE */
296             new_mtime = buf.st_mtime;
297
298         utim.actime = new_atime;
299         utim.modtime = new_mtime;
300
301         res = backend_utime(path, &utim);
302         if (res == -1)
303             return setattr_err();
304     }
305
306     return NFS3_OK;
307 }
308
309 /*
310  * race unsafe setting of attributes
311  */
312 static nfsstat3 set_attr_unsafe(const char *path, nfs_fh3 nfh, sattr3 new)
313 {
314     unfs3_fh_t *fh = (void *) nfh.data.data_val;
315     uid_t new_uid;
316     gid_t new_gid;
317     backend_statstruct buf;
318     int res;
319
320     res = backend_lstat(path, &buf);
321     if (res != 0)
322         return NFS3ERR_STALE;
323
324     /* check local fs race */
325     if (buf.st_dev != fh->dev || buf.st_ino != fh->ino)
326         return NFS3ERR_STALE;
327
328     /* set file size */
329     if (new.size.set_it == TRUE) {
330         res = backend_truncate(path, new.size.set_size3_u.size);
331         if (res == -1)
332             return setattr_err();
333     }
334
335     /* set uid and gid */
336     if (new.uid.set_it == TRUE || new.gid.set_it == TRUE) {
337         if (new.uid.set_it == TRUE)
338             new_uid = new.uid.set_uid3_u.uid;
339         else
340             new_uid = -1;
341         if (new_uid == buf.st_uid)
342             new_uid = -1;
343
344         if (new.gid.set_it == TRUE)
345             new_gid = new.gid.set_gid3_u.gid;
346         else
347             new_gid = -1;
348
349         res = backend_lchown(path, new_uid, new_gid);
350         if (res == -1)
351             return setattr_err();
352     }
353
354     /* set mode */
355     if (new.mode.set_it == TRUE) {
356         res = backend_chmod(path, new.mode.set_mode3_u.mode);
357         if (res == -1)
358             return setattr_err();
359     }
360
361     return set_time(path, buf, new);
362 }
363
364 /*
365  * set attributes of an object
366  */
367 nfsstat3 set_attr(const char *path, nfs_fh3 nfh, sattr3 new)
368 {
369     unfs3_fh_t *fh = (void *) nfh.data.data_val;
370     int res, fd;
371     uid_t new_uid;
372     gid_t new_gid;
373     backend_statstruct buf;
374
375     res = backend_lstat(path, &buf);
376     if (res != 0)
377         return NFS3ERR_STALE;
378
379     /* 
380      * don't open(2) device nodes, it could trigger
381      * module loading on the server
382      */
383     if (S_ISBLK(buf.st_mode) || S_ISCHR(buf.st_mode))
384         return set_attr_unsafe(path, nfh, new);
385
386 #ifdef S_ISLNK
387     /* 
388      * opening a symlink would open the underlying file,
389      * don't try to do that
390      */
391     if (S_ISLNK(buf.st_mode))
392         return set_attr_unsafe(path, nfh, new);
393 #endif
394
395     /* 
396      * open object for atomic setting of attributes
397      */
398     fd = backend_open(path, O_WRONLY | O_NONBLOCK);
399     if (fd == -1)
400         fd = backend_open(path, O_RDONLY | O_NONBLOCK);
401
402     if (fd == -1)
403         return set_attr_unsafe(path, nfh, new);
404
405     res = backend_fstat(fd, &buf);
406     if (res == -1) {
407         backend_close(fd);
408         return NFS3ERR_STALE;
409     }
410
411     /* check local fs race */
412     if (fh->dev != buf.st_dev || fh->ino != buf.st_ino ||
413         fh->gen != backend_get_gen(buf, fd, path)) {
414         backend_close(fd);
415         return NFS3ERR_STALE;
416     }
417
418     /* set file size */
419     if (new.size.set_it == TRUE) {
420         res = backend_ftruncate(fd, new.size.set_size3_u.size);
421         if (res == -1) {
422             backend_close(fd);
423             return setattr_err();
424         }
425     }
426
427     /* set uid and gid */
428     if (new.uid.set_it == TRUE || new.gid.set_it == TRUE) {
429         if (new.uid.set_it == TRUE)
430             new_uid = new.uid.set_uid3_u.uid;
431         else
432             new_uid = -1;
433         if (new_uid == buf.st_uid)
434             new_uid = -1;
435
436         if (new.gid.set_it == TRUE)
437             new_gid = new.gid.set_gid3_u.gid;
438         else
439             new_gid = -1;
440
441         res = backend_fchown(fd, new_uid, new_gid);
442         if (res == -1) {
443             backend_close(fd);
444             return setattr_err();
445         }
446     }
447
448     /* set mode */
449     if (new.mode.set_it == TRUE) {
450         res = backend_fchmod(fd, new.mode.set_mode3_u.mode);
451         if (res == -1) {
452             backend_close(fd);
453             return setattr_err();
454         }
455     }
456
457     res = backend_close(fd);
458     if (res == -1) {
459         /* error on close probably means attributes didn't make it */
460         return NFS3ERR_IO;
461     }
462
463     /* finally, set times */
464     return set_time(path, buf, new);
465 }
466
467 /*
468  * deduce mode from given settable attributes
469  * default to rwxrwxr-x if no mode given
470  */
471 mode_t create_mode(sattr3 new)
472 {
473     if (new.mode.set_it == TRUE)
474         return new.mode.set_mode3_u.mode;
475     else
476         return S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP |
477             S_IROTH | S_IXOTH;
478 }
479
480 /*
481  * check whether an sattr3 is settable atomically on a create op
482  */
483 nfsstat3 atomic_attr(sattr3 attr)
484 {
485     uid_t used_uid = mangle_uid(attr.uid.set_uid3_u.uid);
486     gid_t used_gid = mangle_gid(attr.gid.set_gid3_u.gid);
487
488     if ((attr.uid.set_it == TRUE && used_uid != backend_geteuid()) ||
489         (attr.gid.set_it == TRUE && used_gid != backend_getegid()) ||
490         (attr.size.set_it == TRUE && attr.size.set_size3_u.size != 0) ||
491         attr.atime.set_it == SET_TO_CLIENT_TIME ||
492         attr.mtime.set_it == SET_TO_CLIENT_TIME)
493         return NFS3ERR_INVAL;
494     else
495         return NFS3_OK;
496 }