3 * UNFS3 attribute handling
4 * (C) 2004, Pascal Schmidt
5 * see file LICENSE for license details
10 #include <sys/types.h>
29 #include "Config/exports.h"
32 * check whether stat_cache is for a regular file
34 * fh_decomp must be called before to fill the stat cache
40 else if (S_ISREG(st_cache.st_mode))
47 * find stat bit corresponding to given NFS file type
49 mode_t type_to_mode(ftype3 ftype)
73 * post_op_attr for error returns
76 static post_op_attr error_attr = {.attributes_follow = FALSE };
78 static post_op_attr error_attr = { FALSE };
82 * return pre-operation attributes
84 * fh_decomp must be called before to fill the stat cache
86 pre_op_attr get_pre_cached(void)
90 if (!st_cache_valid) {
91 result.attributes_follow = FALSE;
95 result.attributes_follow = TRUE;
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;
107 * compute post-operation attributes given a stat buffer
109 post_op_attr get_post_buf(backend_statstruct buf, struct svc_req * req)
113 result.attributes_follow = TRUE;
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;
122 else if (S_ISLNK(buf.st_mode))
123 result.post_op_attr_u.attributes.type = NF3LNK;
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;
132 result.post_op_attr_u.attributes.type = NF3REG;
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;
144 result.post_op_attr_u.attributes.mode = buf.st_mode & 0xFFFF;
145 result.post_op_attr_u.attributes.nlink = buf.st_nlink;
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();
154 if (req->rq_cred.oa_flavor == AUTH_UNIX) {
155 req_uid = auth->aup_uid;
156 req_gid = auth->aup_gid;
159 if ((buf.st_uid == ruid) || (ruid == 0))
160 result.post_op_attr_u.attributes.uid = req_uid;
162 result.post_op_attr_u.attributes.uid = 0;
164 if ((buf.st_gid == backend_getgid()) || (ruid == 0))
165 result.post_op_attr_u.attributes.gid = req_gid;
167 result.post_op_attr_u.attributes.gid = 0;
170 result.post_op_attr_u.attributes.uid = buf.st_uid;
171 result.post_op_attr_u.attributes.gid = buf.st_gid;
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;
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
184 if (exports_opts & OPT_REMOVABLE) {
185 backend_statstruct epbuf;
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;
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);
201 result.post_op_attr_u.attributes.fileid = buf.st_ino;
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;
214 * lowlevel routine for getting post-operation attributes
216 static post_op_attr get_post_ll(const char *path, uint32 dev, uint64 ino,
219 backend_statstruct buf;
225 res = backend_lstat(path, &buf);
229 /* protect against local fs race */
230 if (dev != buf.st_dev || ino != buf.st_ino)
233 return get_post_buf(buf, req);
237 * return post-operation attributes, using fh for old dev/ino
239 post_op_attr get_post_attr(const char *path, nfs_fh3 nfh,
240 struct svc_req * req)
242 unfs3_fh_t *fh = (void *) nfh.data.data_val;
244 return get_post_ll(path, fh->dev, fh->ino, req);
248 * return post-operation attributes, using stat cache for old dev/ino
250 post_op_attr get_post_stat(const char *path, struct svc_req * req)
252 return get_post_ll(path, st_cache.st_dev, st_cache.st_ino, req);
256 * return post-operation attributes using stat cache
258 * fd_decomp must be called before to fill the stat cache
260 post_op_attr get_post_cached(struct svc_req * req)
265 return get_post_buf(st_cache, req);
269 * setting of time, races with local filesystem
271 * there is no futimes() function in POSIX or Linux
273 static nfsstat3 set_time(const char *path, backend_statstruct buf, sattr3 new)
275 time_t new_atime, new_mtime;
279 /* set atime and mtime */
280 if (new.atime.set_it != DONT_CHANGE || new.mtime.set_it != DONT_CHANGE) {
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;
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;
298 utim.actime = new_atime;
299 utim.modtime = new_mtime;
301 res = backend_utime(path, &utim);
303 return setattr_err();
310 * race unsafe setting of attributes
312 static nfsstat3 set_attr_unsafe(const char *path, nfs_fh3 nfh, sattr3 new)
314 unfs3_fh_t *fh = (void *) nfh.data.data_val;
317 backend_statstruct buf;
320 res = backend_lstat(path, &buf);
322 return NFS3ERR_STALE;
324 /* check local fs race */
325 if (buf.st_dev != fh->dev || buf.st_ino != fh->ino)
326 return NFS3ERR_STALE;
329 if (new.size.set_it == TRUE) {
330 res = backend_truncate(path, new.size.set_size3_u.size);
332 return setattr_err();
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;
341 if (new_uid == buf.st_uid)
344 if (new.gid.set_it == TRUE)
345 new_gid = new.gid.set_gid3_u.gid;
349 res = backend_lchown(path, new_uid, new_gid);
351 return setattr_err();
355 if (new.mode.set_it == TRUE) {
356 res = backend_chmod(path, new.mode.set_mode3_u.mode);
358 return setattr_err();
361 return set_time(path, buf, new);
365 * set attributes of an object
367 nfsstat3 set_attr(const char *path, nfs_fh3 nfh, sattr3 new)
369 unfs3_fh_t *fh = (void *) nfh.data.data_val;
373 backend_statstruct buf;
375 res = backend_lstat(path, &buf);
377 return NFS3ERR_STALE;
380 * don't open(2) device nodes, it could trigger
381 * module loading on the server
383 if (S_ISBLK(buf.st_mode) || S_ISCHR(buf.st_mode))
384 return set_attr_unsafe(path, nfh, new);
388 * opening a symlink would open the underlying file,
389 * don't try to do that
391 if (S_ISLNK(buf.st_mode))
392 return set_attr_unsafe(path, nfh, new);
396 * open object for atomic setting of attributes
398 fd = backend_open(path, O_WRONLY | O_NONBLOCK);
400 fd = backend_open(path, O_RDONLY | O_NONBLOCK);
403 return set_attr_unsafe(path, nfh, new);
405 res = backend_fstat(fd, &buf);
408 return NFS3ERR_STALE;
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)) {
415 return NFS3ERR_STALE;
419 if (new.size.set_it == TRUE) {
420 res = backend_ftruncate(fd, new.size.set_size3_u.size);
423 return setattr_err();
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;
433 if (new_uid == buf.st_uid)
436 if (new.gid.set_it == TRUE)
437 new_gid = new.gid.set_gid3_u.gid;
441 res = backend_fchown(fd, new_uid, new_gid);
444 return setattr_err();
449 if (new.mode.set_it == TRUE) {
450 res = backend_fchmod(fd, new.mode.set_mode3_u.mode);
453 return setattr_err();
457 res = backend_close(fd);
459 /* error on close probably means attributes didn't make it */
463 /* finally, set times */
464 return set_time(path, buf, new);
468 * deduce mode from given settable attributes
469 * default to rwxrwxr-x if no mode given
471 mode_t create_mode(sattr3 new)
473 if (new.mode.set_it == TRUE)
474 return new.mode.set_mode3_u.mode;
476 return S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP |
481 * check whether an sattr3 is settable atomically on a create op
483 nfsstat3 atomic_attr(sattr3 attr)
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);
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;