xref: /dragonfly/sys/vfs/tmpfs/tmpfs_subr.c (revision 2b3f93ea6d1f70880f3e87f3c2cbe0dc0bfc9332)
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 
37 #include <sys/kernel.h>
38 #include <sys/param.h>
39 #include <sys/caps.h>
40 #include <sys/proc.h>
41 #include <sys/stat.h>
42 #include <sys/systm.h>
43 #include <sys/vnode.h>
44 #include <sys/vmmeter.h>
45 #include <sys/malloc.h>
46 
47 #include <vm/vm.h>
48 #include <vm/vm_object.h>
49 #include <vm/vm_page.h>
50 #include <vm/vm_pager.h>
51 #include <vm/vm_extern.h>
52 #include <vm/vm_pageout.h>
53 #include <vm/vm_page2.h>
54 
55 #include <vfs/tmpfs/tmpfs.h>
56 #include <vfs/tmpfs/tmpfs_vnops.h>
57 
58 static ino_t tmpfs_fetch_ino(struct tmpfs_mount *);
59 
60 static int tmpfs_dirtree_compare(struct tmpfs_dirent *a,
61           struct tmpfs_dirent *b);
62 RB_GENERATE(tmpfs_dirtree, tmpfs_dirent, rb_node, tmpfs_dirtree_compare);
63 
64 static int tmpfs_dirtree_compare_cookie(struct tmpfs_dirent *a,
65           struct tmpfs_dirent *b);
66 RB_GENERATE(tmpfs_dirtree_cookie, tmpfs_dirent,
67           rb_cookienode, tmpfs_dirtree_compare_cookie);
68 
69 
70 /* --------------------------------------------------------------------- */
71 
72 /*
73  * Allocates a new node of type 'type' inside the 'tmp' mount point, with
74  * its owner set to 'uid', its group to 'gid' and its mode set to 'mode',
75  * using the credentials of the process 'p'.
76  *
77  * If the node type is set to 'VDIR', then the parent parameter must point
78  * to the parent directory of the node being created.  It may only be NULL
79  * while allocating the root node.
80  *
81  * If the node type is set to 'VBLK' or 'VCHR', then the rdev parameter
82  * specifies the device the node represents.
83  *
84  * If the node type is set to 'VLNK', then the parameter target specifies
85  * the file name of the target file for the symbolic link that is being
86  * created.
87  *
88  * Note that new nodes are retrieved from the available list if it has
89  * items or, if it is empty, from the node pool as long as there is enough
90  * space to create them.
91  *
92  * Returns zero on success or an appropriate error code on failure.
93  */
94 int
tmpfs_alloc_node(struct tmpfs_mount * tmp,enum vtype type,uid_t uid,gid_t gid,mode_t mode,char * target,int rmajor,int rminor,struct tmpfs_node ** node)95 tmpfs_alloc_node(struct tmpfs_mount *tmp, enum vtype type,
96                      uid_t uid, gid_t gid, mode_t mode,
97                      char *target, int rmajor, int rminor,
98                      struct tmpfs_node **node)
99 {
100           struct tmpfs_node *nnode;
101           struct timespec ts;
102           dev_t rdev;
103 
104           KKASSERT(IFF(type == VLNK, target != NULL));
105           KKASSERT(IFF(type == VBLK || type == VCHR, rmajor != VNOVAL));
106 
107           if (tmp->tm_nodes_inuse >= tmp->tm_nodes_max)
108                     return (ENOSPC);
109 
110           nnode = kmalloc_obj(sizeof(struct tmpfs_node), tmp->tm_node_zone,
111                                   M_WAITOK | M_ZERO | M_NULLOK);
112           if (nnode == NULL)
113                     return (ENOSPC);
114           tmpfs_node_init(nnode);
115 
116           /* Generic initialization. */
117           nnode->tn_type = type;
118           vfs_timestamp(&ts);
119           nnode->tn_ctime = nnode->tn_mtime = nnode->tn_atime
120                     = ts.tv_sec;
121           nnode->tn_ctimensec = nnode->tn_mtimensec = nnode->tn_atimensec
122                     = ts.tv_nsec;
123           nnode->tn_uid = uid;
124           nnode->tn_gid = gid;
125           nnode->tn_mode = mode;
126           nnode->tn_id = tmpfs_fetch_ino(tmp);
127           nnode->tn_advlock.init_done = 0;
128           KKASSERT(nnode->tn_links == 0);
129 
130           /* Type-specific initialization. */
131           switch (nnode->tn_type) {
132           case VBLK:
133           case VCHR:
134                     rdev = makeudev(rmajor, rminor);
135                     if (rdev == NOUDEV) {
136                               tmpfs_node_uninit(nnode);
137                               kfree_obj(nnode, tmp->tm_node_zone);
138                               return(EINVAL);
139                     }
140                     nnode->tn_rdev = rdev;
141                     break;
142 
143           case VDIR:
144                     RB_INIT(&nnode->tn_dir.tn_dirtree);
145                     RB_INIT(&nnode->tn_dir.tn_cookietree);
146                     nnode->tn_dir.tn_parent = NULL;
147                     nnode->tn_size = 0;
148                     break;
149 
150           case VFIFO:
151                     /* FALLTHROUGH */
152           case VSOCK:
153                     break;
154 
155           case VLNK:
156                     nnode->tn_size = strlen(target);
157                     nnode->tn_link = kmalloc(nnode->tn_size + 1, tmp->tm_name_zone,
158                                                    M_WAITOK | M_NULLOK);
159                     if (nnode->tn_link == NULL) {
160                               tmpfs_node_uninit(nnode);
161                               kfree_obj(nnode, tmp->tm_node_zone);
162                               return (ENOSPC);
163                     }
164                     bcopy(target, nnode->tn_link, nnode->tn_size);
165                     nnode->tn_link[nnode->tn_size] = '\0';
166                     break;
167 
168           case VREG:
169                     nnode->tn_reg.tn_aobj = swap_pager_alloc(NULL, 0,
170                                                                        VM_PROT_DEFAULT, 0);
171                     nnode->tn_reg.tn_aobj_pages = 0;
172                     nnode->tn_size = 0;
173                     vm_object_set_flag(nnode->tn_reg.tn_aobj, OBJ_NOPAGEIN);
174                     break;
175 
176           default:
177                     panic("tmpfs_alloc_node: type %p %d", nnode, (int)nnode->tn_type);
178           }
179 
180           TMPFS_NODE_LOCK(nnode);
181           TMPFS_LOCK(tmp);
182           LIST_INSERT_HEAD(&tmp->tm_nodes_used, nnode, tn_entries);
183           tmp->tm_nodes_inuse++;
184           TMPFS_UNLOCK(tmp);
185           TMPFS_NODE_UNLOCK(nnode);
186 
187           *node = nnode;
188           return 0;
189 }
190 
191 /* --------------------------------------------------------------------- */
192 
193 /*
194  * Destroys the node pointed to by node from the file system 'tmp'.
195  * If the node does not belong to the given mount point, the results are
196  * unpredicted.
197  *
198  * If the node references a directory; no entries are allowed because
199  * their removal could need a recursive algorithm, something forbidden in
200  * kernel space.  Furthermore, there is not need to provide such
201  * functionality (recursive removal) because the only primitives offered
202  * to the user are the removal of empty directories and the deletion of
203  * individual files.
204  *
205  * Note that nodes are not really deleted; in fact, when a node has been
206  * allocated, it cannot be deleted during the whole life of the file
207  * system.  Instead, they are moved to the available list and remain there
208  * until reused.
209  *
210  * A caller must have TMPFS_NODE_LOCK(node) and this function unlocks it.
211  */
212 void
tmpfs_free_node(struct tmpfs_mount * tmp,struct tmpfs_node * node)213 tmpfs_free_node(struct tmpfs_mount *tmp, struct tmpfs_node *node)
214 {
215           vm_pindex_t pages = 0;
216 
217 #ifdef INVARIANTS
218           TMPFS_ASSERT_ELOCKED(node);
219           KKASSERT(node->tn_vnode == NULL);
220 #endif
221           TMPFS_LOCK(tmp);
222           LIST_REMOVE(node, tn_entries);
223           tmp->tm_nodes_inuse--;
224           TMPFS_UNLOCK(tmp);
225           TMPFS_NODE_UNLOCK(node);  /* Caller has this lock */
226 
227           switch (node->tn_type) {
228           case VNON:
229                     /* Do not do anything.  VNON is provided to let the
230                      * allocation routine clean itself easily by avoiding
231                      * duplicating code in it. */
232                     /* FALLTHROUGH */
233           case VBLK:
234                     /* FALLTHROUGH */
235           case VCHR:
236                     /* FALLTHROUGH */
237                     break;
238           case VDIR:
239                     /*
240                      * The parent link can be NULL if this is the root
241                      * node or if it is a directory node that was rmdir'd.
242                      *
243                      * XXX what if node is a directory which still contains
244                      * directory entries (e.g. due to a forced umount) ?
245                      */
246                     node->tn_size = 0;
247                     KKASSERT(node->tn_dir.tn_parent == NULL);
248 
249                     /*
250                      * If the root node is being destroyed don't leave a
251                      * dangling pointer in tmpfs_mount.
252                      */
253                     if (node == tmp->tm_root)
254                               tmp->tm_root = NULL;
255                     break;
256           case VFIFO:
257                     /* FALLTHROUGH */
258           case VSOCK:
259                     break;
260 
261           case VLNK:
262                     kfree(node->tn_link, tmp->tm_name_zone);
263                     node->tn_link = NULL;
264                     node->tn_size = 0;
265                     break;
266 
267           case VREG:
268                     if (node->tn_reg.tn_aobj != NULL)
269                               vm_object_deallocate(node->tn_reg.tn_aobj);
270                     node->tn_reg.tn_aobj = NULL;
271                     pages = node->tn_reg.tn_aobj_pages;
272                     break;
273 
274           default:
275                     panic("tmpfs_free_node: type %p %d", node, (int)node->tn_type);
276           }
277 
278           /*
279            * Clean up fields as a safety before destroying the entry.
280            */
281           tmpfs_node_uninit(node);
282           kfree_obj(node, tmp->tm_node_zone);
283           /* node is now invalid */
284 
285           if (pages)
286                     atomic_add_long(&tmp->tm_pages_used, -(long)pages);
287 }
288 
289 /* --------------------------------------------------------------------- */
290 
291 /*
292  * Allocates a new directory entry for the node node with a name of name.
293  * The new directory entry is returned in *de.
294  *
295  * The link count of node is increased by one to reflect the new object
296  * referencing it.
297  *
298  * Returns zero on success or an appropriate error code on failure.
299  */
300 int
tmpfs_alloc_dirent(struct tmpfs_mount * tmp,struct tmpfs_node * node,const char * name,uint16_t len,struct tmpfs_dirent ** de)301 tmpfs_alloc_dirent(struct tmpfs_mount *tmp, struct tmpfs_node *node,
302                        const char *name, uint16_t len, struct tmpfs_dirent **de)
303 {
304           struct tmpfs_dirent *nde;
305 
306           nde = kmalloc_obj(sizeof(struct tmpfs_dirent),
307                                 tmp->tm_dirent_zone, M_WAITOK);
308           nde->td_name = kmalloc(len + 1, tmp->tm_name_zone, M_WAITOK | M_NULLOK);
309           if (nde->td_name == NULL) {
310                     kfree_obj(nde, tmp->tm_dirent_zone);
311                     *de = NULL;
312                     return (ENOSPC);
313           }
314           nde->td_namelen = len;
315           bcopy(name, nde->td_name, len);
316           nde->td_name[len] = '\0';
317 
318           nde->td_node = node;
319 
320           atomic_add_int(&node->tn_links, 1);
321 
322           *de = nde;
323 
324           return 0;
325 }
326 
327 /* --------------------------------------------------------------------- */
328 
329 /*
330  * Frees a directory entry.  It is the caller's responsibility to destroy
331  * the node referenced by it if needed.
332  *
333  * The link count of node is decreased by one to reflect the removal of an
334  * object that referenced it.  This only happens if 'node_exists' is true;
335  * otherwise the function will not access the node referred to by the
336  * directory entry, as it may already have been released from the outside.
337  */
338 void
tmpfs_free_dirent(struct tmpfs_mount * tmp,struct tmpfs_dirent * de)339 tmpfs_free_dirent(struct tmpfs_mount *tmp, struct tmpfs_dirent *de)
340 {
341           struct tmpfs_node *node;
342 
343           node = de->td_node;
344 
345           KKASSERT(node->tn_links > 0);
346           atomic_add_int(&node->tn_links, -1);
347 
348           kfree(de->td_name, tmp->tm_name_zone);
349           de->td_namelen = 0;
350           de->td_name = NULL;
351           de->td_node = NULL;
352           kfree_obj(de, tmp->tm_dirent_zone);
353 }
354 
355 /* --------------------------------------------------------------------- */
356 
357 /*
358  * Allocates a new vnode for the node node or returns a new reference to
359  * an existing one if the node had already a vnode referencing it.  The
360  * resulting locked vnode is returned in *vpp.
361  *
362  * Returns zero on success or an appropriate error code on failure.
363  *
364  * The caller must ensure that node cannot go away (usually by holding
365  * the related directory entry).
366  *
367  * If dnode is non-NULL this routine avoids deadlocking against it but
368  * can return EAGAIN.  Caller must try again.  The dnode lock will cycle
369  * in this case, it remains locked on return in all cases.  dnode must
370  * be shared-locked.
371  */
372 int
tmpfs_alloc_vp(struct mount * mp,struct tmpfs_node * dnode,struct tmpfs_node * node,int lkflag,struct vnode ** vpp)373 tmpfs_alloc_vp(struct mount *mp,
374                  struct tmpfs_node *dnode, struct tmpfs_node *node, int lkflag,
375                  struct vnode **vpp)
376 {
377           int error = 0;
378           struct vnode *vp;
379 
380 loop:
381           vp = NULL;
382           if (node->tn_vnode == NULL) {
383                     error = getnewvnode(VT_TMPFS, mp, &vp,
384                                             VLKTIMEOUT, LK_CANRECURSE);
385                     if (error)
386                               goto out;
387           }
388 
389           /*
390            * Interlocked extraction from node.  This can race many things.
391            * We have to get a soft reference on the vnode while we hold
392            * the node locked, then acquire it properly and check for races.
393            */
394           TMPFS_NODE_LOCK(node);
395           if (node->tn_vnode) {
396                     if (vp) {
397                               vp->v_type = VBAD;
398                               vx_put(vp);
399                     }
400                     vp = node->tn_vnode;
401 
402                     KKASSERT((node->tn_vpstate & TMPFS_VNODE_DOOMED) == 0);
403                     vhold(vp);
404                     TMPFS_NODE_UNLOCK(node);
405 
406                     if (dnode) {
407                               /*
408                                * Special-case handling to avoid deadlocking against
409                                * dnode.  This case has been validated and occurs
410                                * every so often during synth builds and in other
411                                * situations.
412                                */
413                               if (vget(vp, (lkflag & ~LK_RETRY) |
414                                              LK_NOWAIT |
415                                              LK_EXCLUSIVE) != 0)
416                               {
417                                         TMPFS_NODE_UNLOCK(dnode);
418                                         if (vget(vp, (lkflag & ~LK_RETRY) |
419                                                        LK_SLEEPFAIL |
420                                                        LK_EXCLUSIVE) == 0) {
421                                                   vput(vp);
422                                         }
423                                         vdrop(vp);
424                                         TMPFS_NODE_LOCK_SH(dnode);
425 
426                                         return EAGAIN;
427                               }
428                     } else {
429                               /*
430                                * Normal path
431                                */
432                               if (vget(vp, lkflag | LK_EXCLUSIVE) != 0) {
433                                         vdrop(vp);
434                                         goto loop;
435                               }
436                     }
437                     if (node->tn_vnode != vp) {
438                               vput(vp);
439                               vdrop(vp);
440                               goto loop;
441                     }
442                     vdrop(vp);
443                     goto out;
444           }
445 
446           /*
447            * We need to assign node->tn_vnode.  If vp is NULL, loop up to
448            * allocate the vp.  This can happen due to SMP races.
449            */
450           if (vp == NULL) {
451                     TMPFS_NODE_UNLOCK(node);
452                     goto loop;
453           }
454 
455           /*
456            * This should never happen.
457            */
458           if (node->tn_vpstate & TMPFS_VNODE_DOOMED) {
459                     TMPFS_NODE_UNLOCK(node);
460                     vp->v_type = VBAD;
461                     vx_put(vp);
462                     error = ENOENT;
463                     goto out;
464           }
465 
466           KKASSERT(node->tn_vnode == NULL);
467           KKASSERT(vp != NULL);
468           vp->v_data = node;
469           vp->v_type = node->tn_type;
470 
471           /* Type-specific initialization. */
472           switch (node->tn_type) {
473           case VBLK:
474                     /* FALLTHROUGH */
475           case VCHR:
476                     /* FALLTHROUGH */
477           case VSOCK:
478                     break;
479           case VREG:
480                     /*
481                      * VMIO is mandatory.  Tmpfs also supports KVABIO
482                      * for its tmpfs_strategy().
483                      */
484                     vsetflags(vp, VKVABIO);
485                     vinitvmio(vp, node->tn_size, node->tn_blksize, -1);
486                     break;
487           case VLNK:
488                     break;
489           case VFIFO:
490                     vp->v_ops = &mp->mnt_vn_fifo_ops;
491                     break;
492           case VDIR:
493                     break;
494 
495           default:
496                     panic("tmpfs_alloc_vp: type %p %d", node, (int)node->tn_type);
497           }
498 
499           node->tn_vnode = vp;
500           TMPFS_NODE_UNLOCK(node);
501 
502           vx_downgrade(vp);
503 out:
504           *vpp = vp;
505           KKASSERT(IFF(error == 0, *vpp != NULL && vn_islocked(*vpp)));
506 
507           return error;
508 }
509 
510 /* --------------------------------------------------------------------- */
511 
512 /*
513  * Allocates a new file of type 'type' and adds it to the parent directory
514  * 'dvp'; this addition is done using the component name given in 'cnp'.
515  * The ownership of the new file is automatically assigned based on the
516  * credentials of the caller (through 'cnp'), the group is set based on
517  * the parent directory and the mode is determined from the 'vap' argument.
518  * If successful, *vpp holds a vnode to the newly created file and zero
519  * is returned.  Otherwise *vpp is NULL and the function returns an
520  * appropriate error code.
521  */
522 int
tmpfs_alloc_file(struct vnode * dvp,struct vnode ** vpp,struct vattr * vap,struct namecache * ncp,struct ucred * cred,char * target)523 tmpfs_alloc_file(struct vnode *dvp, struct vnode **vpp, struct vattr *vap,
524                      struct namecache *ncp, struct ucred *cred, char *target)
525 {
526           int error;
527           struct tmpfs_dirent *de;
528           struct tmpfs_mount *tmp;
529           struct tmpfs_node *dnode;
530           struct tmpfs_node *node;
531 
532           tmp = VFS_TO_TMPFS(dvp->v_mount);
533           dnode = VP_TO_TMPFS_DIR(dvp);
534           *vpp = NULL;
535 
536           TMPFS_NODE_LOCK(dnode);
537 
538           /*
539            * If the directory was removed but a process was CD'd into it,
540            * we do not allow any more file/dir creation within it.  Otherwise
541            * we will lose track of it.
542            */
543           KKASSERT(dnode->tn_type == VDIR);
544           if (dnode != tmp->tm_root && dnode->tn_dir.tn_parent == NULL) {
545                     TMPFS_NODE_UNLOCK(dnode);
546                     return ENOENT;
547           }
548 
549           /*
550            * Make sure the link count does not overflow.
551            */
552           if (vap->va_type == VDIR && dnode->tn_links >= LINK_MAX) {
553                     TMPFS_NODE_UNLOCK(dnode);
554                     return EMLINK;
555           }
556 
557           /* Allocate a node that represents the new file. */
558           error = tmpfs_alloc_node(tmp, vap->va_type, cred->cr_uid,
559                                          dnode->tn_gid, vap->va_mode, target,
560                                          vap->va_rmajor, vap->va_rminor, &node);
561           if (error != 0) {
562                     TMPFS_NODE_UNLOCK(dnode);
563                     return error;
564           }
565           TMPFS_NODE_LOCK(node);
566 
567           /* Allocate a directory entry that points to the new file. */
568           error = tmpfs_alloc_dirent(tmp, node, ncp->nc_name, ncp->nc_nlen, &de);
569           if (error != 0) {
570                     TMPFS_NODE_UNLOCK(dnode);
571                     tmpfs_free_node(tmp, node);
572                     /* eats node lock */
573                     return error;
574           }
575 
576           /* Allocate a vnode for the new file. */
577           error = tmpfs_alloc_vp(dvp->v_mount, NULL, node, LK_EXCLUSIVE, vpp);
578           if (error != 0) {
579                     TMPFS_NODE_UNLOCK(dnode);
580                     tmpfs_free_dirent(tmp, de);
581                     tmpfs_free_node(tmp, node);
582                     /* eats node lock */
583                     return error;
584           }
585 
586           /*
587            * Now that all required items are allocated, we can proceed to
588            * insert the new node into the directory, an operation that
589            * cannot fail.
590            */
591           tmpfs_dir_attach_locked(dnode, de);
592           TMPFS_NODE_UNLOCK(dnode);
593           TMPFS_NODE_UNLOCK(node);
594 
595           return error;
596 }
597 
598 /* --------------------------------------------------------------------- */
599 
600 /*
601  * Attaches the directory entry de to the directory represented by dnode.
602  * Note that this does not change the link count of the node pointed by
603  * the directory entry, as this is done by tmpfs_alloc_dirent.
604  *
605  * dnode must be locked.
606  */
607 void
tmpfs_dir_attach_locked(struct tmpfs_node * dnode,struct tmpfs_dirent * de)608 tmpfs_dir_attach_locked(struct tmpfs_node *dnode, struct tmpfs_dirent *de)
609 {
610           struct tmpfs_node *node = de->td_node;
611           struct tmpfs_dirent *de2;
612 
613           if (node && node->tn_type == VDIR) {
614                     TMPFS_NODE_LOCK(node);
615                     atomic_add_int(&node->tn_links, 1);
616                     node->tn_status |= TMPFS_NODE_CHANGED;
617                     node->tn_dir.tn_parent = dnode;
618                     atomic_add_int(&dnode->tn_links, 1);
619                     TMPFS_NODE_UNLOCK(node);
620           }
621           de2 = RB_INSERT(tmpfs_dirtree, &dnode->tn_dir.tn_dirtree, de);
622           KASSERT(de2 == NULL,
623                     ("tmpfs_dir_attach_lockedA: duplicate insertion of %p, has %p\n",
624                     de, de2));
625           de2 = RB_INSERT(tmpfs_dirtree_cookie, &dnode->tn_dir.tn_cookietree, de);
626           KASSERT(de2 == NULL,
627                     ("tmpfs_dir_attach_lockedB: duplicate insertion of %p, has %p\n",
628                     de, de2));
629           dnode->tn_size += sizeof(struct tmpfs_dirent);
630           dnode->tn_status |= TMPFS_NODE_ACCESSED | TMPFS_NODE_CHANGED |
631                                   TMPFS_NODE_MODIFIED;
632 }
633 
634 /* --------------------------------------------------------------------- */
635 
636 /*
637  * Detaches the directory entry de from the directory represented by dnode.
638  * Note that this does not change the link count of the node pointed by
639  * the directory entry, as this is done by tmpfs_free_dirent.
640  *
641  * dnode must be locked.
642  */
643 void
tmpfs_dir_detach_locked(struct tmpfs_node * dnode,struct tmpfs_dirent * de)644 tmpfs_dir_detach_locked(struct tmpfs_node *dnode, struct tmpfs_dirent *de)
645 {
646           struct tmpfs_node *node = de->td_node;
647 
648           RB_REMOVE(tmpfs_dirtree, &dnode->tn_dir.tn_dirtree, de);
649           RB_REMOVE(tmpfs_dirtree_cookie, &dnode->tn_dir.tn_cookietree, de);
650           dnode->tn_size -= sizeof(struct tmpfs_dirent);
651           dnode->tn_status |= TMPFS_NODE_ACCESSED | TMPFS_NODE_CHANGED |
652                                   TMPFS_NODE_MODIFIED;
653 
654           /*
655            * Clean out the tn_parent pointer immediately when removing a
656            * directory.
657            *
658            * Removal of the parent linkage also cleans out the extra tn_links
659            * count we had on both node and dnode.
660            *
661            * node can be NULL (typ during a forced umount), in which case
662            * the mount code is dealing with the linkages from a linked list
663            * scan.
664            */
665           if (node && node->tn_type == VDIR && node->tn_dir.tn_parent) {
666                     TMPFS_NODE_LOCK(node);
667                     KKASSERT(node->tn_dir.tn_parent == dnode);
668                     atomic_add_int(&dnode->tn_links, -1);
669                     atomic_add_int(&node->tn_links, -1);
670                     node->tn_dir.tn_parent = NULL;
671                     TMPFS_NODE_UNLOCK(node);
672           }
673 }
674 
675 /* --------------------------------------------------------------------- */
676 
677 /*
678  * Looks for a directory entry in the directory represented by node.
679  * 'ncp' describes the name of the entry to look for.  Note that the .
680  * and .. components are not allowed as they do not physically exist
681  * within directories.
682  *
683  * Returns a pointer to the entry when found, otherwise NULL.
684  *
685  * Caller must hold the node locked (shared ok)
686  */
687 struct tmpfs_dirent *
tmpfs_dir_lookup(struct tmpfs_node * node,struct tmpfs_node * f,struct namecache * ncp)688 tmpfs_dir_lookup(struct tmpfs_node *node, struct tmpfs_node *f,
689                      struct namecache *ncp)
690 {
691           struct tmpfs_dirent *de;
692           int len = ncp->nc_nlen;
693           struct tmpfs_dirent wanted;
694 
695           wanted.td_namelen = len;
696           wanted.td_name = ncp->nc_name;
697 
698           TMPFS_VALIDATE_DIR(node);
699 
700           de = RB_FIND(tmpfs_dirtree, &node->tn_dir.tn_dirtree, &wanted);
701 
702           KASSERT((f == NULL || de == NULL || f == de->td_node),
703                     ("tmpfs_dir_lookup: Incorrect node %p %p %p",
704                      f, de, (de ? de->td_node : NULL)));
705 
706           return de;
707 }
708 
709 /* --------------------------------------------------------------------- */
710 
711 /*
712  * Helper function for tmpfs_readdir.  Creates a '.' entry for the given
713  * directory and returns it in the uio space.  The function returns 0
714  * on success, -1 if there was not enough space in the uio structure to
715  * hold the directory entry or an appropriate error code if another
716  * error happens.
717  */
718 int
tmpfs_dir_getdotdent(struct tmpfs_node * node,struct uio * uio)719 tmpfs_dir_getdotdent(struct tmpfs_node *node, struct uio *uio)
720 {
721           int error;
722 
723           TMPFS_VALIDATE_DIR(node);
724           KKASSERT(uio->uio_offset == TMPFS_DIRCOOKIE_DOT);
725 
726           if (vop_write_dirent(&error, uio, node->tn_id, DT_DIR, 1, "."))
727                     return -1;
728           if (error == 0)
729                     uio->uio_offset = TMPFS_DIRCOOKIE_DOTDOT;
730           return error;
731 }
732 
733 /* --------------------------------------------------------------------- */
734 
735 /*
736  * Helper function for tmpfs_readdir.  Creates a '..' entry for the given
737  * directory and returns it in the uio space.  The function returns 0
738  * on success, -1 if there was not enough space in the uio structure to
739  * hold the directory entry or an appropriate error code if another
740  * error happens.
741  */
742 int
tmpfs_dir_getdotdotdent(struct tmpfs_mount * tmp,struct tmpfs_node * node,struct uio * uio)743 tmpfs_dir_getdotdotdent(struct tmpfs_mount *tmp, struct tmpfs_node *node,
744                               struct uio *uio)
745 {
746           int error;
747           ino_t d_ino;
748 
749           TMPFS_VALIDATE_DIR(node);
750           KKASSERT(uio->uio_offset == TMPFS_DIRCOOKIE_DOTDOT);
751 
752           if (node->tn_dir.tn_parent) {
753                     TMPFS_NODE_LOCK(node);
754                     if (node->tn_dir.tn_parent)
755                               d_ino = node->tn_dir.tn_parent->tn_id;
756                     else
757                               d_ino = tmp->tm_root->tn_id;
758                     TMPFS_NODE_UNLOCK(node);
759           } else {
760                     d_ino = tmp->tm_root->tn_id;
761           }
762 
763           if (vop_write_dirent(&error, uio, d_ino, DT_DIR, 2, ".."))
764                     return -1;
765           if (error == 0) {
766                     struct tmpfs_dirent *de;
767                     de = RB_MIN(tmpfs_dirtree_cookie, &node->tn_dir.tn_cookietree);
768                     if (de == NULL)
769                               uio->uio_offset = TMPFS_DIRCOOKIE_EOF;
770                     else
771                               uio->uio_offset = tmpfs_dircookie(de);
772           }
773           return error;
774 }
775 
776 /* --------------------------------------------------------------------- */
777 
778 /*
779  * Lookup a directory entry by its associated cookie.
780  *
781  * Must be called with the directory node locked (shared ok)
782  */
783 #if 0
784 
785 struct lubycookie_info {
786           off_t     cookie;
787           struct tmpfs_dirent *de;
788 };
789 
790 static int
791 lubycookie_cmp(struct tmpfs_dirent *de, void *arg)
792 {
793           struct lubycookie_info *info = arg;
794           off_t cookie = tmpfs_dircookie(de);
795 
796           if (cookie < info->cookie)
797                     return(-1);
798           if (cookie > info->cookie)
799                     return(1);
800           return(0);
801 }
802 
803 static int
804 lubycookie_callback(struct tmpfs_dirent *de, void *arg)
805 {
806           struct lubycookie_info *info = arg;
807 
808           if (tmpfs_dircookie(de) == info->cookie) {
809                     info->de = de;
810                     return(-1);
811           }
812           return(0);
813 }
814 
815 #endif
816 
817 /*
818  * Find first cookie >= (cookie).  If exact specified, find the exact
819  * cookie.
820  */
821 struct tmpfs_dirent *
tmpfs_dir_lookupbycookie(struct tmpfs_node * node,off_t cookie,int exact)822 tmpfs_dir_lookupbycookie(struct tmpfs_node *node, off_t cookie, int exact)
823 {
824 #if 0
825           struct lubycookie_info info;
826 
827           info.cookie = cookie;
828           info.de = NULL;
829           RB_SCAN(tmpfs_dirtree_cookie, &node->tn_dir.tn_cookietree,
830                     lubycookie_cmp, lubycookie_callback, &info);
831           return (info.de);
832 #endif
833           struct tmpfs_dirent *cdent = tmpfs_cookiedir(cookie);
834           struct tmpfs_dirent *last;
835           struct tmpfs_dirent *tmp;
836 
837           last = NULL;
838           tmp = RB_ROOT(&node->tn_dir.tn_cookietree);
839           while (tmp) {
840                     if (cdent == tmp)
841                               return cdent;
842                     if (cdent > tmp) {
843                               last = tmp;
844                               tmp = RB_RIGHT(tmp, rb_cookienode);
845                     } else {
846                               tmp = RB_LEFT(tmp, rb_cookienode);
847                     }
848           }
849           return (exact ? NULL : last);
850 }
851 
852 /* --------------------------------------------------------------------- */
853 
854 /*
855  * Helper function for tmpfs_readdir.  Returns as much directory entries
856  * as can fit in the uio space.  The read starts at uio->uio_offset.
857  * The function returns 0 on success, -1 if there was not enough space
858  * in the uio structure to hold the directory entry or an appropriate
859  * error code if another error happens.
860  *
861  * Caller must hold the node locked (shared ok)
862  */
863 int
tmpfs_dir_getdents(struct tmpfs_node * node,struct uio * uio,off_t * cntp)864 tmpfs_dir_getdents(struct tmpfs_node *node, struct uio *uio, off_t *cntp)
865 {
866           int error;
867           off_t startcookie;
868           struct tmpfs_dirent *de;
869 
870           TMPFS_VALIDATE_DIR(node);
871 
872           /*
873            * Locate the first directory entry we have to return.  We have cached
874            * the last readdir in the node, so use those values if appropriate.
875            * Otherwise do a linear scan to find the requested entry.
876            *
877            * If a particular cookie does not exist, locate the first valid
878            * cookie after that one.
879            */
880           startcookie = uio->uio_offset;
881           KKASSERT(startcookie != TMPFS_DIRCOOKIE_DOT);
882           KKASSERT(startcookie != TMPFS_DIRCOOKIE_DOTDOT);
883 
884           if (startcookie == TMPFS_DIRCOOKIE_EOF)
885                     return 0;
886 
887           /*
888            * Inexact lookup, find first direntry with a cookie >= startcookie.
889            * If none found we are at the EOF.
890            */
891           de = tmpfs_dir_lookupbycookie(node, startcookie, 0);
892           if (de == NULL) {
893                     uio->uio_offset = TMPFS_DIRCOOKIE_EOF;
894                     return 0;
895           }
896 
897           /*
898            * Read as much entries as possible; i.e., until we reach the end of
899            * the directory or we exhaust uio space.
900            */
901           do {
902                     ino_t d_ino;
903                     uint8_t d_type;
904 
905                     /* Create a dirent structure representing the current
906                      * tmpfs_node and fill it. */
907                     d_ino = de->td_node->tn_id;
908                     switch (de->td_node->tn_type) {
909                     case VBLK:
910                               d_type = DT_BLK;
911                               break;
912 
913                     case VCHR:
914                               d_type = DT_CHR;
915                               break;
916 
917                     case VDIR:
918                               d_type = DT_DIR;
919                               break;
920 
921                     case VFIFO:
922                               d_type = DT_FIFO;
923                               break;
924 
925                     case VLNK:
926                               d_type = DT_LNK;
927                               break;
928 
929                     case VREG:
930                               d_type = DT_REG;
931                               break;
932 
933                     case VSOCK:
934                               d_type = DT_SOCK;
935                               break;
936 
937                     default:
938                               panic("tmpfs_dir_getdents: type %p %d",
939                                   de->td_node, (int)de->td_node->tn_type);
940                     }
941                     KKASSERT(de->td_namelen < 256); /* 255 + 1 */
942 
943                     if (vop_write_dirent(&error, uio, d_ino, d_type,
944                         de->td_namelen, de->td_name)) {
945                               error = -1;
946                               break;
947                     }
948 
949                     (*cntp)++;
950                     de = RB_NEXT(tmpfs_dirtree_cookie,
951                                    node->tn_dir.tn_cookietree, de);
952           } while (error == 0 && uio->uio_resid > 0 && de != NULL);
953 
954           /* Update the offset and cache. */
955           if (de == NULL) {
956                     uio->uio_offset = TMPFS_DIRCOOKIE_EOF;
957           } else {
958                     uio->uio_offset = tmpfs_dircookie(de);
959           }
960 
961           return error;
962 }
963 
964 /* --------------------------------------------------------------------- */
965 
966 /*
967  * Resizes the aobj associated to the regular file pointed to by vp to
968  * the size newsize.  'vp' must point to a vnode that represents a regular
969  * file.  'newsize' must be positive.
970  *
971  * pass NVEXTF_TRIVIAL when buf content will be overwritten, otherwise set 0
972  * to be zero filled.
973  *
974  * Returns zero on success or an appropriate error code on failure.
975  *
976  * Caller must hold the node exclusively locked.
977  */
978 int
tmpfs_reg_resize(struct vnode * vp,off_t newsize,int trivial)979 tmpfs_reg_resize(struct vnode *vp, off_t newsize, int trivial)
980 {
981           int error;
982           vm_pindex_t newpages, oldpages;
983           struct tmpfs_mount *tmp;
984           struct tmpfs_node *node;
985           off_t oldsize;
986           int nvextflags;
987 
988 #ifdef INVARIANTS
989           KKASSERT(vp->v_type == VREG);
990           KKASSERT(newsize >= 0);
991 #endif
992 
993           node = VP_TO_TMPFS_NODE(vp);
994           tmp = VFS_TO_TMPFS(vp->v_mount);
995 
996           /*
997            * Convert the old and new sizes to the number of pages needed to
998            * store them.  It may happen that we do not need to do anything
999            * because the last allocated page can accommodate the change on
1000            * its own.
1001            */
1002           oldsize = node->tn_size;
1003           oldpages = round_page64(oldsize) / PAGE_SIZE;
1004           KKASSERT(oldpages == node->tn_reg.tn_aobj_pages);
1005           newpages = round_page64(newsize) / PAGE_SIZE;
1006 
1007           if (newpages > oldpages &&
1008              tmp->tm_pages_used + newpages - oldpages > tmp->tm_pages_max) {
1009                     error = ENOSPC;
1010                     goto out;
1011           }
1012           node->tn_reg.tn_aobj_pages = newpages;
1013           node->tn_size = newsize;
1014 
1015           if (newpages != oldpages)
1016                     atomic_add_long(&tmp->tm_pages_used, (newpages - oldpages));
1017 
1018           /*
1019            * nvextflags to pass along for bdwrite() vs buwrite(), this is
1020            * so tmpfs activity doesn't eat memory being freed by the pageout
1021            * daemon.
1022            */
1023           if (vm_pages_needed || vm_paging_start(0) ||
1024               tmpfs_bufcache_mode >= 2) {
1025                     nvextflags = 0;
1026           } else {
1027                     nvextflags = NVEXTF_BUWRITE;
1028           }
1029 
1030 
1031           /*
1032            * When adjusting the vnode filesize and its VM object we must
1033            * also adjust our backing VM object (aobj).  The blocksize
1034            * used must match the block sized we use for the buffer cache.
1035            *
1036            * The backing VM object may contain VM pages as well as swap
1037            * assignments if we previously renamed main object pages into
1038            * it during deactivation.
1039            *
1040            * To make things easier tmpfs uses a blksize in multiples of
1041            * PAGE_SIZE, and will only increase the blksize as a small file
1042            * increases in size.  Once a file has exceeded TMPFS_BLKSIZE (16KB),
1043            * the blksize is maxed out.  Truncating the file does not reduce
1044            * the blksize.
1045            */
1046           if (newsize < oldsize) {
1047                     vm_pindex_t osize;
1048                     vm_pindex_t nsize;
1049                     vm_object_t aobj;
1050 
1051                     error = nvtruncbuf(vp, newsize, node->tn_blksize,
1052                                            -1, nvextflags);
1053                     aobj = node->tn_reg.tn_aobj;
1054                     if (aobj) {
1055                               osize = aobj->size;
1056                               nsize = vp->v_object->size;
1057                               if (nsize < osize) {
1058                                         aobj->size = osize;
1059                                         swap_pager_freespace(aobj, nsize,
1060                                                                  osize - nsize);
1061                                         vm_object_page_remove(aobj, nsize, osize,
1062                                                                   FALSE);
1063                               }
1064                     }
1065           } else {
1066                     vm_object_t aobj;
1067                     int nblksize;
1068 
1069                     /*
1070                      * The first (and only the first) buffer in the file is resized
1071                      * in multiples of PAGE_SIZE, up to TMPFS_BLKSIZE.
1072                      */
1073                     nblksize = node->tn_blksize;
1074                     while (nblksize < TMPFS_BLKSIZE &&
1075                            nblksize < newsize) {
1076                               nblksize += PAGE_SIZE;
1077                     }
1078 
1079                     if (trivial)
1080                               nvextflags |= NVEXTF_TRIVIAL;
1081 
1082                     error = nvextendbuf(vp, oldsize, newsize,
1083                                             node->tn_blksize, nblksize,
1084                                             -1, -1, nvextflags);
1085                     node->tn_blksize = nblksize;
1086                     aobj = node->tn_reg.tn_aobj;
1087                     if (aobj)
1088                               aobj->size = vp->v_object->size;
1089           }
1090 
1091 out:
1092           return error;
1093 }
1094 
1095 /* --------------------------------------------------------------------- */
1096 
1097 /*
1098  * Change flags of the given vnode.
1099  * Caller should execute tmpfs_update on vp after a successful execution.
1100  * The vnode must be locked on entry and remain locked on exit.
1101  */
1102 int
tmpfs_chflags(struct vnode * vp,u_long vaflags,struct ucred * cred)1103 tmpfs_chflags(struct vnode *vp, u_long vaflags, struct ucred *cred)
1104 {
1105           int error;
1106           struct tmpfs_node *node;
1107           int flags;
1108 
1109           KKASSERT(vn_islocked(vp));
1110 
1111           node = VP_TO_TMPFS_NODE(vp);
1112           flags = node->tn_flags;
1113 
1114           /* Disallow this operation if the file system is mounted read-only. */
1115           if (vp->v_mount->mnt_flag & MNT_RDONLY)
1116                     return EROFS;
1117           error = vop_helper_setattr_flags(&flags, vaflags, node->tn_uid, cred);
1118 
1119           /* Actually change the flags on the node itself */
1120           if (error == 0) {
1121                     TMPFS_NODE_LOCK(node);
1122                     node->tn_flags = flags;
1123                     node->tn_status |= TMPFS_NODE_CHANGED;
1124                     TMPFS_NODE_UNLOCK(node);
1125           }
1126 
1127           KKASSERT(vn_islocked(vp));
1128 
1129           return error;
1130 }
1131 
1132 /* --------------------------------------------------------------------- */
1133 
1134 /*
1135  * Change access mode on the given vnode.
1136  * Caller should execute tmpfs_update on vp after a successful execution.
1137  * The vnode must be locked on entry and remain locked on exit.
1138  */
1139 int
tmpfs_chmod(struct vnode * vp,mode_t vamode,struct ucred * cred)1140 tmpfs_chmod(struct vnode *vp, mode_t vamode, struct ucred *cred)
1141 {
1142           struct tmpfs_node *node;
1143           mode_t cur_mode;
1144           int error;
1145 
1146           KKASSERT(vn_islocked(vp));
1147 
1148           node = VP_TO_TMPFS_NODE(vp);
1149 
1150           /* Disallow this operation if the file system is mounted read-only. */
1151           if (vp->v_mount->mnt_flag & MNT_RDONLY)
1152                     return EROFS;
1153 
1154           /* Immutable or append-only files cannot be modified, either. */
1155           if (node->tn_flags & (IMMUTABLE | APPEND))
1156                     return EPERM;
1157 
1158           cur_mode = node->tn_mode;
1159           error = vop_helper_chmod(vp, vamode, cred, node->tn_uid, node->tn_gid,
1160                                          &cur_mode);
1161 
1162           if (error == 0 &&
1163               (node->tn_mode & ALLPERMS) != (cur_mode & ALLPERMS)) {
1164                     TMPFS_NODE_LOCK(node);
1165                     node->tn_mode &= ~ALLPERMS;
1166                     node->tn_mode |= cur_mode & ALLPERMS;
1167 
1168                     node->tn_status |= TMPFS_NODE_CHANGED;
1169                     TMPFS_NODE_UNLOCK(node);
1170           }
1171 
1172           KKASSERT(vn_islocked(vp));
1173 
1174           return 0;
1175 }
1176 
1177 /* --------------------------------------------------------------------- */
1178 
1179 /*
1180  * Change ownership of the given vnode.  At least one of uid or gid must
1181  * be different than VNOVAL.  If one is set to that value, the attribute
1182  * is unchanged.
1183  * Caller should execute tmpfs_update on vp after a successful execution.
1184  * The vnode must be locked on entry and remain locked on exit.
1185  */
1186 int
tmpfs_chown(struct vnode * vp,uid_t uid,gid_t gid,struct ucred * cred)1187 tmpfs_chown(struct vnode *vp, uid_t uid, gid_t gid, struct ucred *cred)
1188 {
1189           mode_t cur_mode;
1190           uid_t cur_uid;
1191           gid_t cur_gid;
1192           struct tmpfs_node *node;
1193           int error;
1194 
1195           KKASSERT(vn_islocked(vp));
1196           node = VP_TO_TMPFS_NODE(vp);
1197 
1198           /* Disallow this operation if the file system is mounted read-only. */
1199           if (vp->v_mount->mnt_flag & MNT_RDONLY)
1200                     return EROFS;
1201 
1202           /* Immutable or append-only files cannot be modified, either. */
1203           if (node->tn_flags & (IMMUTABLE | APPEND))
1204                     return EPERM;
1205 
1206           cur_uid = node->tn_uid;
1207           cur_gid = node->tn_gid;
1208           cur_mode = node->tn_mode;
1209           error = vop_helper_chown(vp, uid, gid, cred,
1210                                          &cur_uid, &cur_gid, &cur_mode);
1211 
1212           if (error == 0) {
1213                     TMPFS_NODE_LOCK(node);
1214                     if (cur_uid != node->tn_uid ||
1215                         cur_gid != node->tn_gid ||
1216                         cur_mode != node->tn_mode) {
1217                               node->tn_uid = cur_uid;
1218                               node->tn_gid = cur_gid;
1219                               node->tn_mode = cur_mode;
1220                               node->tn_status |= TMPFS_NODE_CHANGED;
1221                     }
1222                     TMPFS_NODE_UNLOCK(node);
1223           }
1224 
1225           return error;
1226 }
1227 
1228 /* --------------------------------------------------------------------- */
1229 
1230 /*
1231  * Change size of the given vnode.
1232  * Caller should execute tmpfs_update on vp after a successful execution.
1233  * The vnode must be locked on entry and remain locked on exit.
1234  */
1235 int
tmpfs_chsize(struct vnode * vp,u_quad_t size,struct ucred * cred)1236 tmpfs_chsize(struct vnode *vp, u_quad_t size, struct ucred *cred)
1237 {
1238           int error;
1239           struct tmpfs_node *node;
1240 
1241           KKASSERT(vn_islocked(vp));
1242 
1243           node = VP_TO_TMPFS_NODE(vp);
1244 
1245           /* Decide whether this is a valid operation based on the file type. */
1246           error = 0;
1247           switch (vp->v_type) {
1248           case VDIR:
1249                     return EISDIR;
1250 
1251           case VREG:
1252                     if (vp->v_mount->mnt_flag & MNT_RDONLY)
1253                               return EROFS;
1254                     break;
1255 
1256           case VBLK:
1257                     /* FALLTHROUGH */
1258           case VCHR:
1259                     /* FALLTHROUGH */
1260           case VFIFO:
1261                     /* Allow modifications of special files even if in the file
1262                      * system is mounted read-only (we are not modifying the
1263                      * files themselves, but the objects they represent). */
1264                     return 0;
1265 
1266           default:
1267                     /* Anything else is unsupported. */
1268                     return EOPNOTSUPP;
1269           }
1270 
1271           /* Immutable or append-only files cannot be modified, either. */
1272           if (node->tn_flags & (IMMUTABLE | APPEND))
1273                     return EPERM;
1274 
1275           error = tmpfs_truncate(vp, size);
1276           /* tmpfs_truncate will raise the NOTE_EXTEND and NOTE_ATTRIB kevents
1277            * for us, as will update tn_status; no need to do that here. */
1278 
1279           KKASSERT(vn_islocked(vp));
1280 
1281           return error;
1282 }
1283 
1284 /* --------------------------------------------------------------------- */
1285 
1286 /*
1287  * Change access and modification times of the given vnode.
1288  * Caller should execute tmpfs_update on vp after a successful execution.
1289  * The vnode must be locked on entry and remain locked on exit.
1290  */
1291 int
tmpfs_chtimes(struct vnode * vp,struct timespec * atime,struct timespec * mtime,int vaflags,struct ucred * cred)1292 tmpfs_chtimes(struct vnode *vp, struct timespec *atime, struct timespec *mtime,
1293                 int vaflags, struct ucred *cred)
1294 {
1295           struct tmpfs_node *node;
1296 
1297           KKASSERT(vn_islocked(vp));
1298 
1299           node = VP_TO_TMPFS_NODE(vp);
1300 
1301           /* Disallow this operation if the file system is mounted read-only. */
1302           if (vp->v_mount->mnt_flag & MNT_RDONLY)
1303                     return EROFS;
1304 
1305           /* Immutable or append-only files cannot be modified, either. */
1306           if (node->tn_flags & (IMMUTABLE | APPEND))
1307                     return EPERM;
1308 
1309           TMPFS_NODE_LOCK(node);
1310           if (atime->tv_sec != VNOVAL && atime->tv_nsec != VNOVAL)
1311                     node->tn_status |= TMPFS_NODE_ACCESSED;
1312 
1313           if (mtime->tv_sec != VNOVAL && mtime->tv_nsec != VNOVAL) {
1314                     node->tn_status |= TMPFS_NODE_MODIFIED;
1315                     vclrflags(vp, VLASTWRITETS);
1316           }
1317 
1318           TMPFS_NODE_UNLOCK(node);
1319 
1320           tmpfs_itimes(vp, atime, mtime);
1321 
1322           KKASSERT(vn_islocked(vp));
1323 
1324           return 0;
1325 }
1326 
1327 /* --------------------------------------------------------------------- */
1328 /* Sync timestamps */
1329 void
tmpfs_itimes(struct vnode * vp,const struct timespec * acc,const struct timespec * mod)1330 tmpfs_itimes(struct vnode *vp, const struct timespec *acc,
1331                const struct timespec *mod)
1332 {
1333           struct tmpfs_node *node;
1334           struct timespec now;
1335 
1336           node = VP_TO_TMPFS_NODE(vp);
1337 
1338           if ((node->tn_status & (TMPFS_NODE_ACCESSED | TMPFS_NODE_MODIFIED |
1339               TMPFS_NODE_CHANGED)) == 0) {
1340                     return;
1341           }
1342 
1343           vfs_timestamp(&now);
1344 
1345           TMPFS_NODE_LOCK(node);
1346           if (node->tn_status & TMPFS_NODE_ACCESSED) {
1347                     if (acc == NULL)
1348                                acc = &now;
1349                     node->tn_atime = acc->tv_sec;
1350                     node->tn_atimensec = acc->tv_nsec;
1351           }
1352           if (node->tn_status & TMPFS_NODE_MODIFIED) {
1353                     if (mod == NULL)
1354                               mod = &now;
1355                     node->tn_mtime = mod->tv_sec;
1356                     node->tn_mtimensec = mod->tv_nsec;
1357           }
1358           if (node->tn_status & TMPFS_NODE_CHANGED) {
1359                     node->tn_ctime = now.tv_sec;
1360                     node->tn_ctimensec = now.tv_nsec;
1361           }
1362 
1363           node->tn_status &= ~(TMPFS_NODE_ACCESSED |
1364                                    TMPFS_NODE_MODIFIED |
1365                                    TMPFS_NODE_CHANGED);
1366           TMPFS_NODE_UNLOCK(node);
1367 }
1368 
1369 /* --------------------------------------------------------------------- */
1370 
1371 void
tmpfs_update(struct vnode * vp)1372 tmpfs_update(struct vnode *vp)
1373 {
1374           tmpfs_itimes(vp, NULL, NULL);
1375 }
1376 
1377 /* --------------------------------------------------------------------- */
1378 
1379 /*
1380  * Caller must hold an exclusive node lock.
1381  */
1382 int
tmpfs_truncate(struct vnode * vp,off_t length)1383 tmpfs_truncate(struct vnode *vp, off_t length)
1384 {
1385           int error;
1386           struct tmpfs_node *node;
1387 
1388           node = VP_TO_TMPFS_NODE(vp);
1389 
1390           if (length < 0) {
1391                     error = EINVAL;
1392                     goto out;
1393           }
1394 
1395           if (node->tn_size == length) {
1396                     error = 0;
1397                     goto out;
1398           }
1399 
1400           if (length > VFS_TO_TMPFS(vp->v_mount)->tm_maxfilesize)
1401                     return (EFBIG);
1402 
1403 
1404           error = tmpfs_reg_resize(vp, length, 1);
1405 
1406           if (error == 0)
1407                     node->tn_status |= TMPFS_NODE_CHANGED | TMPFS_NODE_MODIFIED;
1408 
1409 out:
1410           tmpfs_update(vp);
1411 
1412           return error;
1413 }
1414 
1415 /* --------------------------------------------------------------------- */
1416 
1417 static ino_t
tmpfs_fetch_ino(struct tmpfs_mount * tmp)1418 tmpfs_fetch_ino(struct tmpfs_mount *tmp)
1419 {
1420           ino_t ret;
1421 
1422           ret = atomic_fetchadd_64(&tmp->tm_ino, 1);
1423 
1424           return (ret);
1425 }
1426 
1427 static int
tmpfs_dirtree_compare(struct tmpfs_dirent * a,struct tmpfs_dirent * b)1428 tmpfs_dirtree_compare(struct tmpfs_dirent *a, struct tmpfs_dirent *b)
1429 {
1430           if (a->td_namelen > b->td_namelen)
1431                     return 1;
1432           else if (a->td_namelen < b->td_namelen)
1433                     return -1;
1434           else
1435                     return strncmp(a->td_name, b->td_name, a->td_namelen);
1436 }
1437 
1438 static int
tmpfs_dirtree_compare_cookie(struct tmpfs_dirent * a,struct tmpfs_dirent * b)1439 tmpfs_dirtree_compare_cookie(struct tmpfs_dirent *a, struct tmpfs_dirent *b)
1440 {
1441           if (a < b)
1442                     return(-1);
1443           if (a > b)
1444                     return(1);
1445           return 0;
1446 }
1447 
1448 /*
1449  * Lock for rename.  The namecache entries for the related terminal files
1450  * are already locked but the directories are not.  A directory lock order
1451  * reversal is possible so use a deterministic order.
1452  *
1453  * Generally order path parent-to-child or using a simple pointer comparison.
1454  * Probably not perfect but it should catch most of the cases.
1455  *
1456  * Underlying files must be locked after the related directory.
1457  */
1458 void
tmpfs_lock4(struct tmpfs_node * node1,struct tmpfs_node * node2,struct tmpfs_node * node3,struct tmpfs_node * node4)1459 tmpfs_lock4(struct tmpfs_node *node1, struct tmpfs_node *node2,
1460               struct tmpfs_node *node3, struct tmpfs_node *node4)
1461 {
1462           if (node1->tn_dir.tn_parent != node2 &&
1463               (node1 < node2 || node2->tn_dir.tn_parent == node1)) {
1464                     TMPFS_NODE_LOCK(node1);                 /* fdir */
1465                     TMPFS_NODE_LOCK(node3);                 /* ffile */
1466                     TMPFS_NODE_LOCK(node2);                 /* tdir */
1467                     if (node4)
1468                               TMPFS_NODE_LOCK(node4);       /* tfile */
1469           } else {
1470                     TMPFS_NODE_LOCK(node2);                 /* tdir */
1471                     if (node4)
1472                               TMPFS_NODE_LOCK(node4);       /* tfile */
1473                     TMPFS_NODE_LOCK(node1);                 /* fdir */
1474                     TMPFS_NODE_LOCK(node3);                 /* ffile */
1475           }
1476 }
1477 
1478 void
tmpfs_unlock4(struct tmpfs_node * node1,struct tmpfs_node * node2,struct tmpfs_node * node3,struct tmpfs_node * node4)1479 tmpfs_unlock4(struct tmpfs_node *node1, struct tmpfs_node *node2,
1480                 struct tmpfs_node *node3, struct tmpfs_node *node4)
1481 {
1482           if (node4)
1483                     TMPFS_NODE_UNLOCK(node4);
1484           TMPFS_NODE_UNLOCK(node2);
1485           TMPFS_NODE_UNLOCK(node3);
1486           TMPFS_NODE_UNLOCK(node1);
1487 }
1488