]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - sys/fs/tmpfs/tmpfs_subr.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / sys / fs / tmpfs / tmpfs_subr.c
1 /*      $NetBSD: tmpfs_subr.c,v 1.35 2007/07/09 21:10:50 ad Exp $       */
2
3 /*-
4  * Copyright (c) 2005 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Julio M. Merino Vidal, developed as part of Google's Summer of Code
9  * 2005 program.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  */
32
33 /*
34  * Efficient memory file system supporting functions.
35  */
36 #include <sys/cdefs.h>
37 __FBSDID("$FreeBSD$");
38
39 #include <sys/param.h>
40 #include <sys/namei.h>
41 #include <sys/priv.h>
42 #include <sys/proc.h>
43 #include <sys/stat.h>
44 #include <sys/systm.h>
45 #include <sys/sysctl.h>
46 #include <sys/vnode.h>
47 #include <sys/vmmeter.h>
48
49 #include <vm/vm.h>
50 #include <vm/vm_param.h>
51 #include <vm/vm_object.h>
52 #include <vm/vm_page.h>
53 #include <vm/vm_pageout.h>
54 #include <vm/vm_pager.h>
55 #include <vm/vm_extern.h>
56
57 #include <fs/tmpfs/tmpfs.h>
58 #include <fs/tmpfs/tmpfs_fifoops.h>
59 #include <fs/tmpfs/tmpfs_vnops.h>
60
61 SYSCTL_NODE(_vfs, OID_AUTO, tmpfs, CTLFLAG_RW, 0, "tmpfs file system");
62
63 static long tmpfs_pages_reserved = TMPFS_PAGES_MINRESERVED;
64
65 static int
66 sysctl_mem_reserved(SYSCTL_HANDLER_ARGS)
67 {
68         int error;
69         long pages, bytes;
70
71         pages = *(long *)arg1;
72         bytes = pages * PAGE_SIZE;
73
74         error = sysctl_handle_long(oidp, &bytes, 0, req);
75         if (error || !req->newptr)
76                 return (error);
77
78         pages = bytes / PAGE_SIZE;
79         if (pages < TMPFS_PAGES_MINRESERVED)
80                 return (EINVAL);
81
82         *(long *)arg1 = pages;
83         return (0);
84 }
85
86 SYSCTL_PROC(_vfs_tmpfs, OID_AUTO, memory_reserved, CTLTYPE_LONG|CTLFLAG_RW,
87     &tmpfs_pages_reserved, 0, sysctl_mem_reserved, "L",
88     "Amount of available memory and swap below which tmpfs growth stops");
89
90 size_t
91 tmpfs_mem_avail(void)
92 {
93         vm_ooffset_t avail;
94
95         avail = swap_pager_avail + cnt.v_free_count + cnt.v_cache_count -
96             tmpfs_pages_reserved;
97         if (__predict_false(avail < 0))
98                 avail = 0;
99         return (avail);
100 }
101
102 size_t
103 tmpfs_pages_used(struct tmpfs_mount *tmp)
104 {
105         const size_t node_size = sizeof(struct tmpfs_node) +
106             sizeof(struct tmpfs_dirent);
107         size_t meta_pages;
108
109         meta_pages = howmany((uintmax_t)tmp->tm_nodes_inuse * node_size,
110             PAGE_SIZE);
111         return (meta_pages + tmp->tm_pages_used);
112 }
113
114 static size_t
115 tmpfs_pages_check_avail(struct tmpfs_mount *tmp, size_t req_pages)
116 {
117         if (tmpfs_mem_avail() < req_pages)
118                 return (0);
119
120         if (tmp->tm_pages_max != SIZE_MAX &&
121             tmp->tm_pages_max < req_pages + tmpfs_pages_used(tmp))
122                         return (0);
123
124         return (1);
125 }
126
127 /* --------------------------------------------------------------------- */
128
129 /*
130  * Allocates a new node of type 'type' inside the 'tmp' mount point, with
131  * its owner set to 'uid', its group to 'gid' and its mode set to 'mode',
132  * using the credentials of the process 'p'.
133  *
134  * If the node type is set to 'VDIR', then the parent parameter must point
135  * to the parent directory of the node being created.  It may only be NULL
136  * while allocating the root node.
137  *
138  * If the node type is set to 'VBLK' or 'VCHR', then the rdev parameter
139  * specifies the device the node represents.
140  *
141  * If the node type is set to 'VLNK', then the parameter target specifies
142  * the file name of the target file for the symbolic link that is being
143  * created.
144  *
145  * Note that new nodes are retrieved from the available list if it has
146  * items or, if it is empty, from the node pool as long as there is enough
147  * space to create them.
148  *
149  * Returns zero on success or an appropriate error code on failure.
150  */
151 int
152 tmpfs_alloc_node(struct tmpfs_mount *tmp, enum vtype type,
153     uid_t uid, gid_t gid, mode_t mode, struct tmpfs_node *parent,
154     char *target, dev_t rdev, struct tmpfs_node **node)
155 {
156         struct tmpfs_node *nnode;
157
158         /* If the root directory of the 'tmp' file system is not yet
159          * allocated, this must be the request to do it. */
160         MPASS(IMPLIES(tmp->tm_root == NULL, parent == NULL && type == VDIR));
161
162         MPASS(IFF(type == VLNK, target != NULL));
163         MPASS(IFF(type == VBLK || type == VCHR, rdev != VNOVAL));
164
165         if (tmp->tm_nodes_inuse >= tmp->tm_nodes_max)
166                 return (ENOSPC);
167         if (tmpfs_pages_check_avail(tmp, 1) == 0)
168                 return (ENOSPC);
169
170         nnode = (struct tmpfs_node *)uma_zalloc_arg(
171                                 tmp->tm_node_pool, tmp, M_WAITOK);
172
173         /* Generic initialization. */
174         nnode->tn_type = type;
175         vfs_timestamp(&nnode->tn_atime);
176         nnode->tn_birthtime = nnode->tn_ctime = nnode->tn_mtime =
177             nnode->tn_atime;
178         nnode->tn_uid = uid;
179         nnode->tn_gid = gid;
180         nnode->tn_mode = mode;
181         nnode->tn_id = alloc_unr(tmp->tm_ino_unr);
182
183         /* Type-specific initialization. */
184         switch (nnode->tn_type) {
185         case VBLK:
186         case VCHR:
187                 nnode->tn_rdev = rdev;
188                 break;
189
190         case VDIR:
191                 TAILQ_INIT(&nnode->tn_dir.tn_dirhead);
192                 MPASS(parent != nnode);
193                 MPASS(IMPLIES(parent == NULL, tmp->tm_root == NULL));
194                 nnode->tn_dir.tn_parent = (parent == NULL) ? nnode : parent;
195                 nnode->tn_dir.tn_readdir_lastn = 0;
196                 nnode->tn_dir.tn_readdir_lastp = NULL;
197                 nnode->tn_links++;
198                 TMPFS_NODE_LOCK(nnode->tn_dir.tn_parent);
199                 nnode->tn_dir.tn_parent->tn_links++;
200                 TMPFS_NODE_UNLOCK(nnode->tn_dir.tn_parent);
201                 break;
202
203         case VFIFO:
204                 /* FALLTHROUGH */
205         case VSOCK:
206                 break;
207
208         case VLNK:
209                 MPASS(strlen(target) < MAXPATHLEN);
210                 nnode->tn_size = strlen(target);
211                 nnode->tn_link = malloc(nnode->tn_size, M_TMPFSNAME,
212                     M_WAITOK);
213                 memcpy(nnode->tn_link, target, nnode->tn_size);
214                 break;
215
216         case VREG:
217                 nnode->tn_reg.tn_aobj =
218                     vm_pager_allocate(OBJT_SWAP, NULL, 0, VM_PROT_DEFAULT, 0,
219                         NULL /* XXXKIB - tmpfs needs swap reservation */);
220                 break;
221
222         default:
223                 panic("tmpfs_alloc_node: type %p %d", nnode, (int)nnode->tn_type);
224         }
225
226         TMPFS_LOCK(tmp);
227         LIST_INSERT_HEAD(&tmp->tm_nodes_used, nnode, tn_entries);
228         tmp->tm_nodes_inuse++;
229         TMPFS_UNLOCK(tmp);
230
231         *node = nnode;
232         return 0;
233 }
234
235 /* --------------------------------------------------------------------- */
236
237 /*
238  * Destroys the node pointed to by node from the file system 'tmp'.
239  * If the node does not belong to the given mount point, the results are
240  * unpredicted.
241  *
242  * If the node references a directory; no entries are allowed because
243  * their removal could need a recursive algorithm, something forbidden in
244  * kernel space.  Furthermore, there is not need to provide such
245  * functionality (recursive removal) because the only primitives offered
246  * to the user are the removal of empty directories and the deletion of
247  * individual files.
248  *
249  * Note that nodes are not really deleted; in fact, when a node has been
250  * allocated, it cannot be deleted during the whole life of the file
251  * system.  Instead, they are moved to the available list and remain there
252  * until reused.
253  */
254 void
255 tmpfs_free_node(struct tmpfs_mount *tmp, struct tmpfs_node *node)
256 {
257         vm_object_t uobj;
258
259 #ifdef INVARIANTS
260         TMPFS_NODE_LOCK(node);
261         MPASS(node->tn_vnode == NULL);
262         MPASS((node->tn_vpstate & TMPFS_VNODE_ALLOCATING) == 0);
263         TMPFS_NODE_UNLOCK(node);
264 #endif
265
266         TMPFS_LOCK(tmp);
267         LIST_REMOVE(node, tn_entries);
268         tmp->tm_nodes_inuse--;
269         TMPFS_UNLOCK(tmp);
270
271         switch (node->tn_type) {
272         case VNON:
273                 /* Do not do anything.  VNON is provided to let the
274                  * allocation routine clean itself easily by avoiding
275                  * duplicating code in it. */
276                 /* FALLTHROUGH */
277         case VBLK:
278                 /* FALLTHROUGH */
279         case VCHR:
280                 /* FALLTHROUGH */
281         case VDIR:
282                 /* FALLTHROUGH */
283         case VFIFO:
284                 /* FALLTHROUGH */
285         case VSOCK:
286                 break;
287
288         case VLNK:
289                 free(node->tn_link, M_TMPFSNAME);
290                 break;
291
292         case VREG:
293                 uobj = node->tn_reg.tn_aobj;
294                 if (uobj != NULL) {
295                         TMPFS_LOCK(tmp);
296                         tmp->tm_pages_used -= uobj->size;
297                         TMPFS_UNLOCK(tmp);
298                         vm_object_deallocate(uobj);
299                 }
300                 break;
301
302         default:
303                 panic("tmpfs_free_node: type %p %d", node, (int)node->tn_type);
304         }
305
306         free_unr(tmp->tm_ino_unr, node->tn_id);
307         uma_zfree(tmp->tm_node_pool, node);
308 }
309
310 /* --------------------------------------------------------------------- */
311
312 /*
313  * Allocates a new directory entry for the node node with a name of name.
314  * The new directory entry is returned in *de.
315  *
316  * The link count of node is increased by one to reflect the new object
317  * referencing it.
318  *
319  * Returns zero on success or an appropriate error code on failure.
320  */
321 int
322 tmpfs_alloc_dirent(struct tmpfs_mount *tmp, struct tmpfs_node *node,
323     const char *name, uint16_t len, struct tmpfs_dirent **de)
324 {
325         struct tmpfs_dirent *nde;
326
327         nde = (struct tmpfs_dirent *)uma_zalloc(
328                                         tmp->tm_dirent_pool, M_WAITOK);
329         nde->td_name = malloc(len, M_TMPFSNAME, M_WAITOK);
330         nde->td_namelen = len;
331         memcpy(nde->td_name, name, len);
332
333         nde->td_node = node;
334         if (node != NULL)
335                 node->tn_links++;
336
337         *de = nde;
338
339         return 0;
340 }
341
342 /* --------------------------------------------------------------------- */
343
344 /*
345  * Frees a directory entry.  It is the caller's responsibility to destroy
346  * the node referenced by it if needed.
347  *
348  * The link count of node is decreased by one to reflect the removal of an
349  * object that referenced it.  This only happens if 'node_exists' is true;
350  * otherwise the function will not access the node referred to by the
351  * directory entry, as it may already have been released from the outside.
352  */
353 void
354 tmpfs_free_dirent(struct tmpfs_mount *tmp, struct tmpfs_dirent *de,
355     boolean_t node_exists)
356 {
357         if (node_exists) {
358                 struct tmpfs_node *node;
359
360                 node = de->td_node;
361                 if (node != NULL) {
362                         MPASS(node->tn_links > 0);
363                         node->tn_links--;
364                 }
365         }
366
367         free(de->td_name, M_TMPFSNAME);
368         uma_zfree(tmp->tm_dirent_pool, de);
369 }
370
371 /* --------------------------------------------------------------------- */
372
373 /*
374  * Allocates a new vnode for the node node or returns a new reference to
375  * an existing one if the node had already a vnode referencing it.  The
376  * resulting locked vnode is returned in *vpp.
377  *
378  * Returns zero on success or an appropriate error code on failure.
379  */
380 int
381 tmpfs_alloc_vp(struct mount *mp, struct tmpfs_node *node, int lkflag,
382     struct vnode **vpp)
383 {
384         int error = 0;
385         struct vnode *vp;
386
387 loop:
388         TMPFS_NODE_LOCK(node);
389         if ((vp = node->tn_vnode) != NULL) {
390                 MPASS((node->tn_vpstate & TMPFS_VNODE_DOOMED) == 0);
391                 VI_LOCK(vp);
392                 TMPFS_NODE_UNLOCK(node);
393                 error = vget(vp, lkflag | LK_INTERLOCK, curthread);
394                 if (error != 0) {
395                         vp = NULL;
396                         goto out;
397                 }
398
399                 /*
400                  * Make sure the vnode is still there after
401                  * getting the interlock to avoid racing a free.
402                  */
403                 if (node->tn_vnode == NULL || node->tn_vnode != vp) {
404                         vput(vp);
405                         goto loop;
406                 }
407
408                 goto out;
409         }
410
411         if ((node->tn_vpstate & TMPFS_VNODE_DOOMED) ||
412             (node->tn_type == VDIR && node->tn_dir.tn_parent == NULL)) {
413                 TMPFS_NODE_UNLOCK(node);
414                 error = ENOENT;
415                 vp = NULL;
416                 goto out;
417         }
418
419         /*
420          * otherwise lock the vp list while we call getnewvnode
421          * since that can block.
422          */
423         if (node->tn_vpstate & TMPFS_VNODE_ALLOCATING) {
424                 node->tn_vpstate |= TMPFS_VNODE_WANT;
425                 error = msleep((caddr_t) &node->tn_vpstate,
426                     TMPFS_NODE_MTX(node), PDROP | PCATCH,
427                     "tmpfs_alloc_vp", 0);
428                 if (error)
429                         return error;
430
431                 goto loop;
432         } else
433                 node->tn_vpstate |= TMPFS_VNODE_ALLOCATING;
434         
435         TMPFS_NODE_UNLOCK(node);
436
437         /* Get a new vnode and associate it with our node. */
438         error = getnewvnode("tmpfs", mp, &tmpfs_vnodeop_entries, &vp);
439         if (error != 0)
440                 goto unlock;
441         MPASS(vp != NULL);
442
443         (void) vn_lock(vp, lkflag | LK_RETRY);
444
445         vp->v_data = node;
446         vp->v_type = node->tn_type;
447
448         /* Type-specific initialization. */
449         switch (node->tn_type) {
450         case VBLK:
451                 /* FALLTHROUGH */
452         case VCHR:
453                 /* FALLTHROUGH */
454         case VLNK:
455                 /* FALLTHROUGH */
456         case VREG:
457                 /* FALLTHROUGH */
458         case VSOCK:
459                 break;
460         case VFIFO:
461                 vp->v_op = &tmpfs_fifoop_entries;
462                 break;
463         case VDIR:
464                 MPASS(node->tn_dir.tn_parent != NULL);
465                 if (node->tn_dir.tn_parent == node)
466                         vp->v_vflag |= VV_ROOT;
467                 break;
468
469         default:
470                 panic("tmpfs_alloc_vp: type %p %d", node, (int)node->tn_type);
471         }
472
473         vnode_pager_setsize(vp, node->tn_size);
474         error = insmntque(vp, mp);
475         if (error)
476                 vp = NULL;
477
478 unlock:
479         TMPFS_NODE_LOCK(node);
480
481         MPASS(node->tn_vpstate & TMPFS_VNODE_ALLOCATING);
482         node->tn_vpstate &= ~TMPFS_VNODE_ALLOCATING;
483         node->tn_vnode = vp;
484
485         if (node->tn_vpstate & TMPFS_VNODE_WANT) {
486                 node->tn_vpstate &= ~TMPFS_VNODE_WANT;
487                 TMPFS_NODE_UNLOCK(node);
488                 wakeup((caddr_t) &node->tn_vpstate);
489         } else
490                 TMPFS_NODE_UNLOCK(node);
491
492 out:
493         *vpp = vp;
494
495 #ifdef INVARIANTS
496         if (error == 0) {
497                 MPASS(*vpp != NULL && VOP_ISLOCKED(*vpp));
498                 TMPFS_NODE_LOCK(node);
499                 MPASS(*vpp == node->tn_vnode);
500                 TMPFS_NODE_UNLOCK(node);
501         }
502 #endif
503
504         return error;
505 }
506
507 /* --------------------------------------------------------------------- */
508
509 /*
510  * Destroys the association between the vnode vp and the node it
511  * references.
512  */
513 void
514 tmpfs_free_vp(struct vnode *vp)
515 {
516         struct tmpfs_node *node;
517
518         node = VP_TO_TMPFS_NODE(vp);
519
520         mtx_assert(TMPFS_NODE_MTX(node), MA_OWNED);
521         node->tn_vnode = NULL;
522         vp->v_data = NULL;
523 }
524
525 /* --------------------------------------------------------------------- */
526
527 /*
528  * Allocates a new file of type 'type' and adds it to the parent directory
529  * 'dvp'; this addition is done using the component name given in 'cnp'.
530  * The ownership of the new file is automatically assigned based on the
531  * credentials of the caller (through 'cnp'), the group is set based on
532  * the parent directory and the mode is determined from the 'vap' argument.
533  * If successful, *vpp holds a vnode to the newly created file and zero
534  * is returned.  Otherwise *vpp is NULL and the function returns an
535  * appropriate error code.
536  */
537 int
538 tmpfs_alloc_file(struct vnode *dvp, struct vnode **vpp, struct vattr *vap,
539     struct componentname *cnp, char *target)
540 {
541         int error;
542         struct tmpfs_dirent *de;
543         struct tmpfs_mount *tmp;
544         struct tmpfs_node *dnode;
545         struct tmpfs_node *node;
546         struct tmpfs_node *parent;
547
548         MPASS(VOP_ISLOCKED(dvp));
549         MPASS(cnp->cn_flags & HASBUF);
550
551         tmp = VFS_TO_TMPFS(dvp->v_mount);
552         dnode = VP_TO_TMPFS_DIR(dvp);
553         *vpp = NULL;
554
555         /* If the entry we are creating is a directory, we cannot overflow
556          * the number of links of its parent, because it will get a new
557          * link. */
558         if (vap->va_type == VDIR) {
559                 /* Ensure that we do not overflow the maximum number of links
560                  * imposed by the system. */
561                 MPASS(dnode->tn_links <= LINK_MAX);
562                 if (dnode->tn_links == LINK_MAX) {
563                         error = EMLINK;
564                         goto out;
565                 }
566
567                 parent = dnode;
568                 MPASS(parent != NULL);
569         } else
570                 parent = NULL;
571
572         /* Allocate a node that represents the new file. */
573         error = tmpfs_alloc_node(tmp, vap->va_type, cnp->cn_cred->cr_uid,
574             dnode->tn_gid, vap->va_mode, parent, target, vap->va_rdev, &node);
575         if (error != 0)
576                 goto out;
577
578         /* Allocate a directory entry that points to the new file. */
579         error = tmpfs_alloc_dirent(tmp, node, cnp->cn_nameptr, cnp->cn_namelen,
580             &de);
581         if (error != 0) {
582                 tmpfs_free_node(tmp, node);
583                 goto out;
584         }
585
586         /* Allocate a vnode for the new file. */
587         error = tmpfs_alloc_vp(dvp->v_mount, node, LK_EXCLUSIVE, vpp);
588         if (error != 0) {
589                 tmpfs_free_dirent(tmp, de, TRUE);
590                 tmpfs_free_node(tmp, node);
591                 goto out;
592         }
593
594         /* Now that all required items are allocated, we can proceed to
595          * insert the new node into the directory, an operation that
596          * cannot fail. */
597         if (cnp->cn_flags & ISWHITEOUT)
598                 tmpfs_dir_whiteout_remove(dvp, cnp);
599         tmpfs_dir_attach(dvp, de);
600
601 out:
602
603         return error;
604 }
605
606 /* --------------------------------------------------------------------- */
607
608 /*
609  * Attaches the directory entry de to the directory represented by vp.
610  * Note that this does not change the link count of the node pointed by
611  * the directory entry, as this is done by tmpfs_alloc_dirent.
612  */
613 void
614 tmpfs_dir_attach(struct vnode *vp, struct tmpfs_dirent *de)
615 {
616         struct tmpfs_node *dnode;
617
618         ASSERT_VOP_ELOCKED(vp, __func__);
619         dnode = VP_TO_TMPFS_DIR(vp);
620         TAILQ_INSERT_TAIL(&dnode->tn_dir.tn_dirhead, de, td_entries);
621         dnode->tn_size += sizeof(struct tmpfs_dirent);
622         dnode->tn_status |= TMPFS_NODE_ACCESSED | TMPFS_NODE_CHANGED | \
623             TMPFS_NODE_MODIFIED;
624 }
625
626 /* --------------------------------------------------------------------- */
627
628 /*
629  * Detaches the directory entry de from the directory represented by vp.
630  * Note that this does not change the link count of the node pointed by
631  * the directory entry, as this is done by tmpfs_free_dirent.
632  */
633 void
634 tmpfs_dir_detach(struct vnode *vp, struct tmpfs_dirent *de)
635 {
636         struct tmpfs_node *dnode;
637
638         ASSERT_VOP_ELOCKED(vp, __func__);
639         dnode = VP_TO_TMPFS_DIR(vp);
640
641         if (dnode->tn_dir.tn_readdir_lastp == de) {
642                 dnode->tn_dir.tn_readdir_lastn = 0;
643                 dnode->tn_dir.tn_readdir_lastp = NULL;
644         }
645
646         TAILQ_REMOVE(&dnode->tn_dir.tn_dirhead, de, td_entries);
647         dnode->tn_size -= sizeof(struct tmpfs_dirent);
648         dnode->tn_status |= TMPFS_NODE_ACCESSED | TMPFS_NODE_CHANGED | \
649             TMPFS_NODE_MODIFIED;
650 }
651
652 /* --------------------------------------------------------------------- */
653
654 /*
655  * Looks for a directory entry in the directory represented by node.
656  * 'cnp' describes the name of the entry to look for.  Note that the .
657  * and .. components are not allowed as they do not physically exist
658  * within directories.
659  *
660  * Returns a pointer to the entry when found, otherwise NULL.
661  */
662 struct tmpfs_dirent *
663 tmpfs_dir_lookup(struct tmpfs_node *node, struct tmpfs_node *f,
664     struct componentname *cnp)
665 {
666         boolean_t found;
667         struct tmpfs_dirent *de;
668
669         MPASS(IMPLIES(cnp->cn_namelen == 1, cnp->cn_nameptr[0] != '.'));
670         MPASS(IMPLIES(cnp->cn_namelen == 2, !(cnp->cn_nameptr[0] == '.' &&
671             cnp->cn_nameptr[1] == '.')));
672         TMPFS_VALIDATE_DIR(node);
673
674         found = 0;
675         TAILQ_FOREACH(de, &node->tn_dir.tn_dirhead, td_entries) {
676                 if (f != NULL && de->td_node != f)
677                     continue;
678                 MPASS(cnp->cn_namelen < 0xffff);
679                 if (de->td_namelen == (uint16_t)cnp->cn_namelen &&
680                     bcmp(de->td_name, cnp->cn_nameptr, de->td_namelen) == 0) {
681                         found = 1;
682                         break;
683                 }
684         }
685         node->tn_status |= TMPFS_NODE_ACCESSED;
686
687         return found ? de : NULL;
688 }
689
690 /* --------------------------------------------------------------------- */
691
692 /*
693  * Helper function for tmpfs_readdir.  Creates a '.' entry for the given
694  * directory and returns it in the uio space.  The function returns 0
695  * on success, -1 if there was not enough space in the uio structure to
696  * hold the directory entry or an appropriate error code if another
697  * error happens.
698  */
699 int
700 tmpfs_dir_getdotdent(struct tmpfs_node *node, struct uio *uio)
701 {
702         int error;
703         struct dirent dent;
704
705         TMPFS_VALIDATE_DIR(node);
706         MPASS(uio->uio_offset == TMPFS_DIRCOOKIE_DOT);
707
708         dent.d_fileno = node->tn_id;
709         dent.d_type = DT_DIR;
710         dent.d_namlen = 1;
711         dent.d_name[0] = '.';
712         dent.d_name[1] = '\0';
713         dent.d_reclen = GENERIC_DIRSIZ(&dent);
714
715         if (dent.d_reclen > uio->uio_resid)
716                 error = -1;
717         else {
718                 error = uiomove(&dent, dent.d_reclen, uio);
719                 if (error == 0)
720                         uio->uio_offset = TMPFS_DIRCOOKIE_DOTDOT;
721         }
722
723         node->tn_status |= TMPFS_NODE_ACCESSED;
724
725         return error;
726 }
727
728 /* --------------------------------------------------------------------- */
729
730 /*
731  * Helper function for tmpfs_readdir.  Creates a '..' entry for the given
732  * directory and returns it in the uio space.  The function returns 0
733  * on success, -1 if there was not enough space in the uio structure to
734  * hold the directory entry or an appropriate error code if another
735  * error happens.
736  */
737 int
738 tmpfs_dir_getdotdotdent(struct tmpfs_node *node, struct uio *uio)
739 {
740         int error;
741         struct dirent dent;
742
743         TMPFS_VALIDATE_DIR(node);
744         MPASS(uio->uio_offset == TMPFS_DIRCOOKIE_DOTDOT);
745
746         /*
747          * Return ENOENT if the current node is already removed.
748          */
749         TMPFS_ASSERT_LOCKED(node);
750         if (node->tn_dir.tn_parent == NULL) {
751                 return (ENOENT);
752         }
753
754         TMPFS_NODE_LOCK(node->tn_dir.tn_parent);
755         dent.d_fileno = node->tn_dir.tn_parent->tn_id;
756         TMPFS_NODE_UNLOCK(node->tn_dir.tn_parent);
757
758         dent.d_type = DT_DIR;
759         dent.d_namlen = 2;
760         dent.d_name[0] = '.';
761         dent.d_name[1] = '.';
762         dent.d_name[2] = '\0';
763         dent.d_reclen = GENERIC_DIRSIZ(&dent);
764
765         if (dent.d_reclen > uio->uio_resid)
766                 error = -1;
767         else {
768                 error = uiomove(&dent, dent.d_reclen, uio);
769                 if (error == 0) {
770                         struct tmpfs_dirent *de;
771
772                         de = TAILQ_FIRST(&node->tn_dir.tn_dirhead);
773                         if (de == NULL)
774                                 uio->uio_offset = TMPFS_DIRCOOKIE_EOF;
775                         else
776                                 uio->uio_offset = tmpfs_dircookie(de);
777                 }
778         }
779
780         node->tn_status |= TMPFS_NODE_ACCESSED;
781
782         return error;
783 }
784
785 /* --------------------------------------------------------------------- */
786
787 /*
788  * Lookup a directory entry by its associated cookie.
789  */
790 struct tmpfs_dirent *
791 tmpfs_dir_lookupbycookie(struct tmpfs_node *node, off_t cookie)
792 {
793         struct tmpfs_dirent *de;
794
795         if (cookie == node->tn_dir.tn_readdir_lastn &&
796             node->tn_dir.tn_readdir_lastp != NULL) {
797                 return node->tn_dir.tn_readdir_lastp;
798         }
799
800         TAILQ_FOREACH(de, &node->tn_dir.tn_dirhead, td_entries) {
801                 if (tmpfs_dircookie(de) == cookie) {
802                         break;
803                 }
804         }
805
806         return de;
807 }
808
809 /* --------------------------------------------------------------------- */
810
811 /*
812  * Helper function for tmpfs_readdir.  Returns as much directory entries
813  * as can fit in the uio space.  The read starts at uio->uio_offset.
814  * The function returns 0 on success, -1 if there was not enough space
815  * in the uio structure to hold the directory entry or an appropriate
816  * error code if another error happens.
817  */
818 int
819 tmpfs_dir_getdents(struct tmpfs_node *node, struct uio *uio, off_t *cntp)
820 {
821         int error;
822         off_t startcookie;
823         struct tmpfs_dirent *de;
824
825         TMPFS_VALIDATE_DIR(node);
826
827         /* Locate the first directory entry we have to return.  We have cached
828          * the last readdir in the node, so use those values if appropriate.
829          * Otherwise do a linear scan to find the requested entry. */
830         startcookie = uio->uio_offset;
831         MPASS(startcookie != TMPFS_DIRCOOKIE_DOT);
832         MPASS(startcookie != TMPFS_DIRCOOKIE_DOTDOT);
833         if (startcookie == TMPFS_DIRCOOKIE_EOF) {
834                 return 0;
835         } else {
836                 de = tmpfs_dir_lookupbycookie(node, startcookie);
837         }
838         if (de == NULL) {
839                 return EINVAL;
840         }
841
842         /* Read as much entries as possible; i.e., until we reach the end of
843          * the directory or we exhaust uio space. */
844         do {
845                 struct dirent d;
846
847                 /* Create a dirent structure representing the current
848                  * tmpfs_node and fill it. */
849                 if (de->td_node == NULL) {
850                         d.d_fileno = 1;
851                         d.d_type = DT_WHT;
852                 } else {
853                         d.d_fileno = de->td_node->tn_id;
854                         switch (de->td_node->tn_type) {
855                         case VBLK:
856                                 d.d_type = DT_BLK;
857                                 break;
858
859                         case VCHR:
860                                 d.d_type = DT_CHR;
861                                 break;
862
863                         case VDIR:
864                                 d.d_type = DT_DIR;
865                                 break;
866
867                         case VFIFO:
868                                 d.d_type = DT_FIFO;
869                                 break;
870
871                         case VLNK:
872                                 d.d_type = DT_LNK;
873                                 break;
874
875                         case VREG:
876                                 d.d_type = DT_REG;
877                                 break;
878
879                         case VSOCK:
880                                 d.d_type = DT_SOCK;
881                                 break;
882
883                         default:
884                                 panic("tmpfs_dir_getdents: type %p %d",
885                                     de->td_node, (int)de->td_node->tn_type);
886                         }
887                 }
888                 d.d_namlen = de->td_namelen;
889                 MPASS(de->td_namelen < sizeof(d.d_name));
890                 (void)memcpy(d.d_name, de->td_name, de->td_namelen);
891                 d.d_name[de->td_namelen] = '\0';
892                 d.d_reclen = GENERIC_DIRSIZ(&d);
893
894                 /* Stop reading if the directory entry we are treating is
895                  * bigger than the amount of data that can be returned. */
896                 if (d.d_reclen > uio->uio_resid) {
897                         error = -1;
898                         break;
899                 }
900
901                 /* Copy the new dirent structure into the output buffer and
902                  * advance pointers. */
903                 error = uiomove(&d, d.d_reclen, uio);
904                 if (error == 0) {
905                         (*cntp)++;
906                         de = TAILQ_NEXT(de, td_entries);
907                 }
908         } while (error == 0 && uio->uio_resid > 0 && de != NULL);
909
910         /* Update the offset and cache. */
911         if (de == NULL) {
912                 uio->uio_offset = TMPFS_DIRCOOKIE_EOF;
913                 node->tn_dir.tn_readdir_lastn = 0;
914                 node->tn_dir.tn_readdir_lastp = NULL;
915         } else {
916                 node->tn_dir.tn_readdir_lastn = uio->uio_offset = tmpfs_dircookie(de);
917                 node->tn_dir.tn_readdir_lastp = de;
918         }
919
920         node->tn_status |= TMPFS_NODE_ACCESSED;
921         return error;
922 }
923
924 int
925 tmpfs_dir_whiteout_add(struct vnode *dvp, struct componentname *cnp)
926 {
927         struct tmpfs_dirent *de;
928         int error;
929
930         error = tmpfs_alloc_dirent(VFS_TO_TMPFS(dvp->v_mount), NULL,
931             cnp->cn_nameptr, cnp->cn_namelen, &de);
932         if (error != 0)
933                 return (error);
934         tmpfs_dir_attach(dvp, de);
935         return (0);
936 }
937
938 void
939 tmpfs_dir_whiteout_remove(struct vnode *dvp, struct componentname *cnp)
940 {
941         struct tmpfs_dirent *de;
942
943         de = tmpfs_dir_lookup(VP_TO_TMPFS_DIR(dvp), NULL, cnp);
944         MPASS(de != NULL && de->td_node == NULL);
945         tmpfs_dir_detach(dvp, de);
946         tmpfs_free_dirent(VFS_TO_TMPFS(dvp->v_mount), de, TRUE);
947 }
948
949 /* --------------------------------------------------------------------- */
950
951 /*
952  * Resizes the aobj associated with the regular file pointed to by 'vp' to the
953  * size 'newsize'.  'vp' must point to a vnode that represents a regular file.
954  * 'newsize' must be positive.
955  *
956  * Returns zero on success or an appropriate error code on failure.
957  */
958 int
959 tmpfs_reg_resize(struct vnode *vp, off_t newsize, boolean_t ignerr)
960 {
961         struct tmpfs_mount *tmp;
962         struct tmpfs_node *node;
963         vm_object_t uobj;
964         vm_page_t m, ma[1];
965         vm_pindex_t idx, newpages, oldpages;
966         off_t oldsize;
967         int base, rv;
968
969         MPASS(vp->v_type == VREG);
970         MPASS(newsize >= 0);
971
972         node = VP_TO_TMPFS_NODE(vp);
973         uobj = node->tn_reg.tn_aobj;
974         tmp = VFS_TO_TMPFS(vp->v_mount);
975
976         /*
977          * Convert the old and new sizes to the number of pages needed to
978          * store them.  It may happen that we do not need to do anything
979          * because the last allocated page can accommodate the change on
980          * its own.
981          */
982         oldsize = node->tn_size;
983         oldpages = OFF_TO_IDX(oldsize + PAGE_MASK);
984         MPASS(oldpages == uobj->size);
985         newpages = OFF_TO_IDX(newsize + PAGE_MASK);
986         if (newpages > oldpages &&
987             tmpfs_pages_check_avail(tmp, newpages - oldpages) == 0)
988                 return (ENOSPC);
989
990         VM_OBJECT_LOCK(uobj);
991         if (newsize < oldsize) {
992                 /*
993                  * Zero the truncated part of the last page.
994                  */
995                 base = newsize & PAGE_MASK;
996                 if (base != 0) {
997                         idx = OFF_TO_IDX(newsize);
998 retry:
999                         m = vm_page_lookup(uobj, idx);
1000                         if (m != NULL) {
1001                                 if ((m->oflags & VPO_BUSY) != 0 ||
1002                                     m->busy != 0) {
1003                                         vm_page_sleep(m, "tmfssz");
1004                                         goto retry;
1005                                 }
1006                                 MPASS(m->valid == VM_PAGE_BITS_ALL);
1007                         } else if (vm_pager_has_page(uobj, idx, NULL, NULL)) {
1008                                 m = vm_page_alloc(uobj, idx, VM_ALLOC_NORMAL);
1009                                 if (m == NULL) {
1010                                         VM_OBJECT_UNLOCK(uobj);
1011                                         VM_WAIT;
1012                                         VM_OBJECT_LOCK(uobj);
1013                                         goto retry;
1014                                 } else if (m->valid != VM_PAGE_BITS_ALL) {
1015                                         ma[0] = m;
1016                                         rv = vm_pager_get_pages(uobj, ma, 1, 0);
1017                                         m = vm_page_lookup(uobj, idx);
1018                                 } else
1019                                         /* A cached page was reactivated. */
1020                                         rv = VM_PAGER_OK;
1021                                 vm_page_lock(m);
1022                                 if (rv == VM_PAGER_OK) {
1023                                         vm_page_deactivate(m);
1024                                         vm_page_unlock(m);
1025                                         vm_page_wakeup(m);
1026                                 } else {
1027                                         vm_page_free(m);
1028                                         vm_page_unlock(m);
1029                                         if (ignerr)
1030                                                 m = NULL;
1031                                         else {
1032                                                 VM_OBJECT_UNLOCK(uobj);
1033                                                 return (EIO);
1034                                         }
1035                                 }
1036                         }
1037                         if (m != NULL) {
1038                                 pmap_zero_page_area(m, base, PAGE_SIZE - base);
1039                                 vm_page_dirty(m);
1040                                 vm_pager_page_unswapped(m);
1041                         }
1042                 }
1043
1044                 /*
1045                  * Release any swap space and free any whole pages.
1046                  */
1047                 if (newpages < oldpages) {
1048                         swap_pager_freespace(uobj, newpages, oldpages -
1049                             newpages);
1050                         vm_object_page_remove(uobj, newpages, 0, 0);
1051                 }
1052         }
1053         uobj->size = newpages;
1054         VM_OBJECT_UNLOCK(uobj);
1055
1056         TMPFS_LOCK(tmp);
1057         tmp->tm_pages_used += (newpages - oldpages);
1058         TMPFS_UNLOCK(tmp);
1059
1060         node->tn_size = newsize;
1061         vnode_pager_setsize(vp, newsize);
1062         return (0);
1063 }
1064
1065 /* --------------------------------------------------------------------- */
1066
1067 /*
1068  * Change flags of the given vnode.
1069  * Caller should execute tmpfs_update on vp after a successful execution.
1070  * The vnode must be locked on entry and remain locked on exit.
1071  */
1072 int
1073 tmpfs_chflags(struct vnode *vp, int flags, struct ucred *cred, struct thread *p)
1074 {
1075         int error;
1076         struct tmpfs_node *node;
1077
1078         MPASS(VOP_ISLOCKED(vp));
1079
1080         node = VP_TO_TMPFS_NODE(vp);
1081
1082         /* Disallow this operation if the file system is mounted read-only. */
1083         if (vp->v_mount->mnt_flag & MNT_RDONLY)
1084                 return EROFS;
1085
1086         /*
1087          * Callers may only modify the file flags on objects they
1088          * have VADMIN rights for.
1089          */
1090         if ((error = VOP_ACCESS(vp, VADMIN, cred, p)))
1091                 return (error);
1092         /*
1093          * Unprivileged processes are not permitted to unset system
1094          * flags, or modify flags if any system flags are set.
1095          */
1096         if (!priv_check_cred(cred, PRIV_VFS_SYSFLAGS, 0)) {
1097                 if (node->tn_flags
1098                   & (SF_NOUNLINK | SF_IMMUTABLE | SF_APPEND)) {
1099                         error = securelevel_gt(cred, 0);
1100                         if (error)
1101                                 return (error);
1102                 }
1103                 /* Snapshot flag cannot be set or cleared */
1104                 if (((flags & SF_SNAPSHOT) != 0 &&
1105                   (node->tn_flags & SF_SNAPSHOT) == 0) ||
1106                   ((flags & SF_SNAPSHOT) == 0 &&
1107                   (node->tn_flags & SF_SNAPSHOT) != 0))
1108                         return (EPERM);
1109                 node->tn_flags = flags;
1110         } else {
1111                 if (node->tn_flags
1112                   & (SF_NOUNLINK | SF_IMMUTABLE | SF_APPEND) ||
1113                   (flags & UF_SETTABLE) != flags)
1114                         return (EPERM);
1115                 node->tn_flags &= SF_SETTABLE;
1116                 node->tn_flags |= (flags & UF_SETTABLE);
1117         }
1118         node->tn_status |= TMPFS_NODE_CHANGED;
1119
1120         MPASS(VOP_ISLOCKED(vp));
1121
1122         return 0;
1123 }
1124
1125 /* --------------------------------------------------------------------- */
1126
1127 /*
1128  * Change access mode on the given vnode.
1129  * Caller should execute tmpfs_update on vp after a successful execution.
1130  * The vnode must be locked on entry and remain locked on exit.
1131  */
1132 int
1133 tmpfs_chmod(struct vnode *vp, mode_t mode, struct ucred *cred, struct thread *p)
1134 {
1135         int error;
1136         struct tmpfs_node *node;
1137
1138         MPASS(VOP_ISLOCKED(vp));
1139
1140         node = VP_TO_TMPFS_NODE(vp);
1141
1142         /* Disallow this operation if the file system is mounted read-only. */
1143         if (vp->v_mount->mnt_flag & MNT_RDONLY)
1144                 return EROFS;
1145
1146         /* Immutable or append-only files cannot be modified, either. */
1147         if (node->tn_flags & (IMMUTABLE | APPEND))
1148                 return EPERM;
1149
1150         /*
1151          * To modify the permissions on a file, must possess VADMIN
1152          * for that file.
1153          */
1154         if ((error = VOP_ACCESS(vp, VADMIN, cred, p)))
1155                 return (error);
1156
1157         /*
1158          * Privileged processes may set the sticky bit on non-directories,
1159          * as well as set the setgid bit on a file with a group that the
1160          * process is not a member of.
1161          */
1162         if (vp->v_type != VDIR && (mode & S_ISTXT)) {
1163                 if (priv_check_cred(cred, PRIV_VFS_STICKYFILE, 0))
1164                         return (EFTYPE);
1165         }
1166         if (!groupmember(node->tn_gid, cred) && (mode & S_ISGID)) {
1167                 error = priv_check_cred(cred, PRIV_VFS_SETGID, 0);
1168                 if (error)
1169                         return (error);
1170         }
1171
1172
1173         node->tn_mode &= ~ALLPERMS;
1174         node->tn_mode |= mode & ALLPERMS;
1175
1176         node->tn_status |= TMPFS_NODE_CHANGED;
1177
1178         MPASS(VOP_ISLOCKED(vp));
1179
1180         return 0;
1181 }
1182
1183 /* --------------------------------------------------------------------- */
1184
1185 /*
1186  * Change ownership of the given vnode.  At least one of uid or gid must
1187  * be different than VNOVAL.  If one is set to that value, the attribute
1188  * is unchanged.
1189  * Caller should execute tmpfs_update on vp after a successful execution.
1190  * The vnode must be locked on entry and remain locked on exit.
1191  */
1192 int
1193 tmpfs_chown(struct vnode *vp, uid_t uid, gid_t gid, struct ucred *cred,
1194     struct thread *p)
1195 {
1196         int error;
1197         struct tmpfs_node *node;
1198         uid_t ouid;
1199         gid_t ogid;
1200
1201         MPASS(VOP_ISLOCKED(vp));
1202
1203         node = VP_TO_TMPFS_NODE(vp);
1204
1205         /* Assign default values if they are unknown. */
1206         MPASS(uid != VNOVAL || gid != VNOVAL);
1207         if (uid == VNOVAL)
1208                 uid = node->tn_uid;
1209         if (gid == VNOVAL)
1210                 gid = node->tn_gid;
1211         MPASS(uid != VNOVAL && gid != VNOVAL);
1212
1213         /* Disallow this operation if the file system is mounted read-only. */
1214         if (vp->v_mount->mnt_flag & MNT_RDONLY)
1215                 return EROFS;
1216
1217         /* Immutable or append-only files cannot be modified, either. */
1218         if (node->tn_flags & (IMMUTABLE | APPEND))
1219                 return EPERM;
1220
1221         /*
1222          * To modify the ownership of a file, must possess VADMIN for that
1223          * file.
1224          */
1225         if ((error = VOP_ACCESS(vp, VADMIN, cred, p)))
1226                 return (error);
1227
1228         /*
1229          * To change the owner of a file, or change the group of a file to a
1230          * group of which we are not a member, the caller must have
1231          * privilege.
1232          */
1233         if ((uid != node->tn_uid ||
1234             (gid != node->tn_gid && !groupmember(gid, cred))) &&
1235             (error = priv_check_cred(cred, PRIV_VFS_CHOWN, 0)))
1236                 return (error);
1237
1238         ogid = node->tn_gid;
1239         ouid = node->tn_uid;
1240
1241         node->tn_uid = uid;
1242         node->tn_gid = gid;
1243
1244         node->tn_status |= TMPFS_NODE_CHANGED;
1245
1246         if ((node->tn_mode & (S_ISUID | S_ISGID)) && (ouid != uid || ogid != gid)) {
1247                 if (priv_check_cred(cred, PRIV_VFS_RETAINSUGID, 0))
1248                         node->tn_mode &= ~(S_ISUID | S_ISGID);
1249         }
1250
1251         MPASS(VOP_ISLOCKED(vp));
1252
1253         return 0;
1254 }
1255
1256 /* --------------------------------------------------------------------- */
1257
1258 /*
1259  * Change size of the given vnode.
1260  * Caller should execute tmpfs_update on vp after a successful execution.
1261  * The vnode must be locked on entry and remain locked on exit.
1262  */
1263 int
1264 tmpfs_chsize(struct vnode *vp, u_quad_t size, struct ucred *cred,
1265     struct thread *p)
1266 {
1267         int error;
1268         struct tmpfs_node *node;
1269
1270         MPASS(VOP_ISLOCKED(vp));
1271
1272         node = VP_TO_TMPFS_NODE(vp);
1273
1274         /* Decide whether this is a valid operation based on the file type. */
1275         error = 0;
1276         switch (vp->v_type) {
1277         case VDIR:
1278                 return EISDIR;
1279
1280         case VREG:
1281                 if (vp->v_mount->mnt_flag & MNT_RDONLY)
1282                         return EROFS;
1283                 break;
1284
1285         case VBLK:
1286                 /* FALLTHROUGH */
1287         case VCHR:
1288                 /* FALLTHROUGH */
1289         case VFIFO:
1290                 /* Allow modifications of special files even if in the file
1291                  * system is mounted read-only (we are not modifying the
1292                  * files themselves, but the objects they represent). */
1293                 return 0;
1294
1295         default:
1296                 /* Anything else is unsupported. */
1297                 return EOPNOTSUPP;
1298         }
1299
1300         /* Immutable or append-only files cannot be modified, either. */
1301         if (node->tn_flags & (IMMUTABLE | APPEND))
1302                 return EPERM;
1303
1304         error = tmpfs_truncate(vp, size);
1305         /* tmpfs_truncate will raise the NOTE_EXTEND and NOTE_ATTRIB kevents
1306          * for us, as will update tn_status; no need to do that here. */
1307
1308         MPASS(VOP_ISLOCKED(vp));
1309
1310         return error;
1311 }
1312
1313 /* --------------------------------------------------------------------- */
1314
1315 /*
1316  * Change access and modification times of the given vnode.
1317  * Caller should execute tmpfs_update on vp after a successful execution.
1318  * The vnode must be locked on entry and remain locked on exit.
1319  */
1320 int
1321 tmpfs_chtimes(struct vnode *vp, struct timespec *atime, struct timespec *mtime,
1322         struct timespec *birthtime, int vaflags, struct ucred *cred, struct thread *l)
1323 {
1324         int error;
1325         struct tmpfs_node *node;
1326
1327         MPASS(VOP_ISLOCKED(vp));
1328
1329         node = VP_TO_TMPFS_NODE(vp);
1330
1331         /* Disallow this operation if the file system is mounted read-only. */
1332         if (vp->v_mount->mnt_flag & MNT_RDONLY)
1333                 return EROFS;
1334
1335         /* Immutable or append-only files cannot be modified, either. */
1336         if (node->tn_flags & (IMMUTABLE | APPEND))
1337                 return EPERM;
1338
1339         /* Determine if the user have proper privilege to update time. */
1340         if (vaflags & VA_UTIMES_NULL) {
1341                 error = VOP_ACCESS(vp, VADMIN, cred, l);
1342                 if (error)
1343                         error = VOP_ACCESS(vp, VWRITE, cred, l);
1344         } else
1345                 error = VOP_ACCESS(vp, VADMIN, cred, l);
1346         if (error)
1347                 return (error);
1348
1349         if (atime->tv_sec != VNOVAL && atime->tv_nsec != VNOVAL)
1350                 node->tn_status |= TMPFS_NODE_ACCESSED;
1351
1352         if (mtime->tv_sec != VNOVAL && mtime->tv_nsec != VNOVAL)
1353                 node->tn_status |= TMPFS_NODE_MODIFIED;
1354
1355         if (birthtime->tv_nsec != VNOVAL && birthtime->tv_nsec != VNOVAL)
1356                 node->tn_status |= TMPFS_NODE_MODIFIED;
1357
1358         tmpfs_itimes(vp, atime, mtime);
1359
1360         if (birthtime->tv_nsec != VNOVAL && birthtime->tv_nsec != VNOVAL)
1361                 node->tn_birthtime = *birthtime;
1362         MPASS(VOP_ISLOCKED(vp));
1363
1364         return 0;
1365 }
1366
1367 /* --------------------------------------------------------------------- */
1368 /* Sync timestamps */
1369 void
1370 tmpfs_itimes(struct vnode *vp, const struct timespec *acc,
1371     const struct timespec *mod)
1372 {
1373         struct tmpfs_node *node;
1374         struct timespec now;
1375
1376         node = VP_TO_TMPFS_NODE(vp);
1377
1378         if ((node->tn_status & (TMPFS_NODE_ACCESSED | TMPFS_NODE_MODIFIED |
1379             TMPFS_NODE_CHANGED)) == 0)
1380                 return;
1381
1382         vfs_timestamp(&now);
1383         if (node->tn_status & TMPFS_NODE_ACCESSED) {
1384                 if (acc == NULL)
1385                          acc = &now;
1386                 node->tn_atime = *acc;
1387         }
1388         if (node->tn_status & TMPFS_NODE_MODIFIED) {
1389                 if (mod == NULL)
1390                         mod = &now;
1391                 node->tn_mtime = *mod;
1392         }
1393         if (node->tn_status & TMPFS_NODE_CHANGED) {
1394                 node->tn_ctime = now;
1395         }
1396         node->tn_status &=
1397             ~(TMPFS_NODE_ACCESSED | TMPFS_NODE_MODIFIED | TMPFS_NODE_CHANGED);
1398 }
1399
1400 /* --------------------------------------------------------------------- */
1401
1402 void
1403 tmpfs_update(struct vnode *vp)
1404 {
1405
1406         tmpfs_itimes(vp, NULL, NULL);
1407 }
1408
1409 /* --------------------------------------------------------------------- */
1410
1411 int
1412 tmpfs_truncate(struct vnode *vp, off_t length)
1413 {
1414         int error;
1415         struct tmpfs_node *node;
1416
1417         node = VP_TO_TMPFS_NODE(vp);
1418
1419         if (length < 0) {
1420                 error = EINVAL;
1421                 goto out;
1422         }
1423
1424         if (node->tn_size == length) {
1425                 error = 0;
1426                 goto out;
1427         }
1428
1429         if (length > VFS_TO_TMPFS(vp->v_mount)->tm_maxfilesize)
1430                 return (EFBIG);
1431
1432         error = tmpfs_reg_resize(vp, length, FALSE);
1433         if (error == 0) {
1434                 node->tn_status |= TMPFS_NODE_CHANGED | TMPFS_NODE_MODIFIED;
1435         }
1436
1437 out:
1438         tmpfs_update(vp);
1439
1440         return error;
1441 }