xref: /dragonfly/sys/vfs/dirfs/dirfs_subr.c (revision fc36a10bce8c5678d103e0498db849506d9dac68)
1 /*
2  * Copyright (c) 2013 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Antonio Huete Jimenez <tuxillo@quantumachine.net>
6  * by Matthew Dillon <dillon@dragonflybsd.org>
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in
16  *    the documentation and/or other materials provided with the
17  *    distribution.
18  * 3. Neither the name of The DragonFly Project nor the names of its
19  *    contributors may be used to endorse or promote products derived
20  *    from this software without specific, prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
26  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
28  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
30  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
31  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
32  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  *
35  */
36 
37 #include <errno.h>
38 #include <fcntl.h>
39 #include <unistd.h>
40 
41 #include <sys/mount.h>
42 #include <sys/queue.h>
43 #include <sys/spinlock2.h>
44 #include <sys/stat.h>
45 #include <sys/systm.h>
46 #include <sys/types.h>
47 #include <sys/vfscache.h>
48 #include <sys/vnode.h>
49 
50 #include "dirfs.h"
51 
52 /*
53  * Allocate and setup all is needed for the dirfs node to hold the filename.
54  * Note: dn_name is NULL terminated.
55  */
56 void
dirfs_node_setname(dirfs_node_t dnp,const char * name,int len)57 dirfs_node_setname(dirfs_node_t dnp, const char *name, int len)
58 {
59           dbg(5, "called\n");
60 
61           if (dnp->dn_name)
62                     kfree(dnp->dn_name, M_DIRFS_MISC);
63           dnp->dn_name = kmalloc(len + 1, M_DIRFS_MISC, M_WAITOK | M_ZERO);
64           bcopy(name, dnp->dn_name, len);
65           dnp->dn_name[len] = 0;
66           dnp->dn_namelen = len;
67 }
68 
69 /*
70  * Allocate enough space to hold a dirfs node structure.
71  * Note: Node name and length isn't handled here.
72  */
73 dirfs_node_t
dirfs_node_alloc(struct mount * mp)74 dirfs_node_alloc(struct mount *mp)
75 {
76         dirfs_node_t dnp;
77 
78         dbg(5, "called\n");
79 
80         dnp = kmalloc(sizeof(*dnp), M_DIRFS_NODE, M_WAITOK | M_ZERO);
81         lockinit(&dnp->dn_lock, "dfsnode", 0, LK_CANRECURSE);
82 
83           dnp->dn_fd = DIRFS_NOFD;
84 
85         return dnp;
86 }
87 
88 /*
89  * Drops a reference to the node and. Node is freed when in the last reference.
90  */
91 void
dirfs_node_drop(dirfs_mount_t dmp,dirfs_node_t dnp)92 dirfs_node_drop(dirfs_mount_t dmp, dirfs_node_t dnp)
93 {
94           dbg(5, "called\n");
95 
96           if (dirfs_node_unref(dnp))
97                     dirfs_node_free(dmp, dnp);
98 }
99 
100 /*
101  * Removes the association with its parent. Before freeing up its resources
102  * the node will be removed from the per-mount passive fd cache and its fd
103  * will be closed, either normally or forced.
104  */
105 int
dirfs_node_free(dirfs_mount_t dmp,dirfs_node_t dnp)106 dirfs_node_free(dirfs_mount_t dmp, dirfs_node_t dnp)
107 {
108           struct vnode *vp;
109 
110           dbg(5, "called\n");
111 
112           KKASSERT(dnp != NULL);
113           debug_node2(dnp);
114 
115           KKASSERT(dirfs_node_refcnt(dnp) == 0);
116 
117           vp = NODE_TO_VP(dnp);
118           /*
119            * Remove the inode from the passive fds list
120            * as we are tearing down the node.
121            * Root inode will be removed on VOP_UNMOUNT()
122            */
123           if (dnp->dn_parent) {         /* NULL when children reaped parents */
124                     dirfs_node_drop(dmp, dnp->dn_parent);
125                     dnp->dn_parent = NULL;
126           }
127           dirfs_node_setpassive(dmp, dnp, 0);
128           if (dnp->dn_name) {
129                     kfree(dnp->dn_name, M_DIRFS_MISC);
130                     dnp->dn_name = NULL;
131           }
132 
133           /*
134            * The file descriptor should have been closed already by the
135            * previous call to dirfs_set-passive. If not, force a sync and
136            * close it.
137            */
138           if (dnp->dn_fd != DIRFS_NOFD) {
139                     if (dnp->dn_vnode)
140                               VOP_FSYNC(vp, MNT_WAIT, 0);
141                     close(dnp->dn_fd);
142                     dnp->dn_fd = DIRFS_NOFD;
143           }
144 
145           lockuninit(&dnp->dn_lock);
146           kfree(dnp, M_DIRFS_NODE);
147           dnp = NULL;
148 
149           return 0;
150 }
151 
152 /*
153  * Do all the operations needed to get a resulting inode <--> host file
154  * association. This or may not include opening the file, which should be
155  * only needed when creating it.
156  *
157  * In the case vap is not NULL and openflags are specified, open the file.
158  */
159 int
dirfs_alloc_file(dirfs_mount_t dmp,dirfs_node_t * dnpp,dirfs_node_t pdnp,struct namecache * ncp,struct vnode ** vpp,struct vattr * vap,int openflags)160 dirfs_alloc_file(dirfs_mount_t dmp, dirfs_node_t *dnpp, dirfs_node_t pdnp,
161     struct namecache *ncp, struct vnode **vpp, struct vattr *vap,
162     int openflags)
163 {
164           dirfs_node_t dnp;
165           dirfs_node_t pathnp;
166           struct vnode *vp;
167           struct mount *mp;
168           char *tmp;
169           char *pathfree;
170           int error;
171 
172           dbg(5, "called\n");
173 
174           error = 0;
175           vp = NULL;
176           mp = DIRFS_TO_VFS(dmp);
177 
178           /* Sanity check */
179           if (pdnp == NULL)
180                     return EINVAL;
181 
182           dnp = dirfs_node_alloc(mp);
183           KKASSERT(dnp != NULL);
184 
185           dirfs_node_lock(dnp);
186           dirfs_node_setname(dnp, ncp->nc_name, ncp->nc_nlen);
187           dnp->dn_parent = pdnp;
188           dirfs_node_ref(pdnp);   /* Children ref */
189           dirfs_node_unlock(dnp);
190 
191           pathnp = dirfs_findfd(dmp, dnp, &tmp, &pathfree);
192 
193           if (openflags && vap != NULL) {
194                     dnp->dn_fd = openat(pathnp->dn_fd, tmp,
195                                             openflags, vap->va_mode);
196                     if (dnp->dn_fd == -1) {
197                               dirfs_dropfd(dmp, pathnp, pathfree);
198                               return errno;
199                     }
200           }
201 
202           error = dirfs_node_stat(pathnp->dn_fd, tmp, dnp);
203           if (error) {                  /* XXX Handle errors */
204                     error = errno;
205                     if (vp)
206                               dirfs_free_vp(dmp, dnp);
207                     dirfs_node_free(dmp, dnp);
208                     dirfs_dropfd(dmp, pathnp, pathfree);
209                     return error;
210           }
211 
212           dirfs_alloc_vp(mp, &vp, LK_CANRECURSE, dnp);
213           *vpp = vp;
214           *dnpp = dnp;
215 
216           dbg(9, "tmp=%s dnp=%p allocated\n", tmp, dnp);
217           dirfs_dropfd(dmp, pathnp, pathfree);
218 
219           /* We want VOP_INACTIVE() to be called on last ref */
220           atomic_set_int(&vp->v_refcnt, VREF_FINALIZE);
221 
222           return error;
223 }
224 
225 /*
226  * Requires an already dirfs_node_t that has been already lstat(2)
227  * for the type comparison
228  */
229 void
dirfs_alloc_vp(struct mount * mp,struct vnode ** vpp,int lkflags,dirfs_node_t dnp)230 dirfs_alloc_vp(struct mount *mp, struct vnode **vpp, int lkflags,
231                  dirfs_node_t dnp)
232 {
233           struct vnode *vp;
234           dirfs_mount_t dmp = VFS_TO_DIRFS(mp);
235 
236           dbg(5, "called\n");
237 
238           /*
239            * Handle vnode reclaim/alloc races
240            */
241           for (;;) {
242                     vp = dnp->dn_vnode;
243                     if (vp) {
244                               if (vget(vp, LK_EXCLUSIVE) == 0)
245                                         break;    /* success */
246                               /* vget raced a reclaim, retry */
247                     } else {
248                               getnewvnode(VT_UNUSED10, mp, &vp, 0, lkflags);
249                               if (dnp->dn_vnode == NULL) {
250                                         dnp->dn_vnode = vp;
251                                         vp->v_data = dnp;
252                                         vp->v_type = dnp->dn_type;
253                                         if (dmp->dm_root == dnp)
254                                                   vsetflags(vp, VROOT);
255                                         dirfs_node_ref(dnp);          /* ref for dnp<->vp */
256 
257                                         /* Type-specific initialization. */
258                                         switch (dnp->dn_type) {
259                                         case VBLK:
260                                         case VCHR:
261                                         case VSOCK:
262                                                   break;
263                                         case VREG:
264                                                   vinitvmio(vp, dnp->dn_size, BMASK, -1);
265                                                   break;
266                                         case VLNK:
267                                                   break;
268                                         case VFIFO:
269                               //              vp->v_ops = &mp->mnt_vn_fifo_ops;
270                                                   break;
271                                         case VDIR:
272                                                   break;
273                                         default:
274                                                   panic("dirfs_alloc_vp: dnp=%p vp=%p "
275                                                         "type=%d",
276                                                         dnp, vp, dnp->dn_type);
277                                                   /* NOT REACHED */
278                                                   break;
279                                         }
280                                         /* downgrade VX lock to VN lock */
281                                         vx_downgrade(vp);
282                                         break;    /* success */
283                               }
284                               vp->v_type = VBAD;
285                               vx_put(vp);
286                               /* multiple dirfs_alloc_vp calls raced, retry */
287                     }
288           }
289           KKASSERT(vp != NULL);
290           *vpp = vp;
291           dbg(9, "dnp=%p vp=%p type=%d\n", dnp, vp, vp->v_type);
292 }
293 
294 /*
295  * Do not call locked!
296  */
297 void
dirfs_free_vp(dirfs_mount_t dmp,dirfs_node_t dnp)298 dirfs_free_vp(dirfs_mount_t dmp, dirfs_node_t dnp)
299 {
300           struct vnode *vp = NODE_TO_VP(dnp);
301 
302           dbg(5, "called\n");
303 
304           dnp->dn_vnode = NULL;
305           vp->v_data = NULL;
306           dirfs_node_drop(dmp, dnp);
307 }
308 
309 int
dirfs_nodetype(struct stat * st)310 dirfs_nodetype(struct stat *st)
311 {
312           int ret;
313           mode_t mode = st->st_mode;
314 
315           if (S_ISDIR(mode))
316                     ret = VDIR;
317           else if (S_ISBLK(mode))
318                     ret = VBLK;
319           else if (S_ISCHR(mode))
320                     ret = VCHR;
321           else if (S_ISFIFO(mode))
322                     ret = VFIFO;
323           else if (S_ISSOCK(mode))
324                     ret = VSOCK;
325           else if (S_ISLNK(mode))
326                     ret = VLNK;
327           else if (S_ISREG(mode))
328                     ret = VREG;
329           else
330                     ret = VBAD;
331 
332           return ret;
333 }
334 
335 int
dirfs_node_stat(int fd,const char * path,dirfs_node_t dnp)336 dirfs_node_stat(int fd, const char *path, dirfs_node_t dnp)
337 {
338           struct stat st;
339           int error;
340 
341           dbg(5, "called\n");
342           if (fd == DIRFS_NOFD)
343                     error = lstat(path, &st);
344           else
345                     error = fstatat(fd, path, &st, AT_SYMLINK_NOFOLLOW);
346 
347           if (error)
348                     return errno;
349 
350           /* Populate our dirfs node struct with stat data */
351           dnp->dn_uid = st.st_uid;
352           dnp->dn_gid = st.st_gid;
353           dnp->dn_mode = st.st_mode;
354           dnp->dn_flags = st.st_flags;
355           dnp->dn_links = st.st_nlink;
356           dnp->dn_atime = st.st_atime;
357           dnp->dn_atimensec = (st.st_atime * 1000000000L);
358           dnp->dn_mtime = st.st_mtime;
359           dnp->dn_mtimensec = (st.st_mtime * 1000000000L);
360           dnp->dn_ctime = st.st_ctime;
361           dnp->dn_ctimensec = (st.st_ctime * 1000000000L);
362           dnp->dn_gen = st.st_gen;
363           dnp->dn_ino = st.st_ino;
364           dnp->dn_st_dev = st.st_dev;
365           dnp->dn_size = st.st_size;
366           dnp->dn_type = dirfs_nodetype(&st);
367 
368           return 0;
369 }
370 
371 char *
dirfs_node_absolute_path(dirfs_mount_t dmp,dirfs_node_t cur,char ** pathfreep)372 dirfs_node_absolute_path(dirfs_mount_t dmp, dirfs_node_t cur, char **pathfreep)
373 {
374           return(dirfs_node_absolute_path_plus(dmp, cur, NULL, pathfreep));
375 }
376 
377 char *
dirfs_node_absolute_path_plus(dirfs_mount_t dmp,dirfs_node_t cur,char * last,char ** pathfreep)378 dirfs_node_absolute_path_plus(dirfs_mount_t dmp, dirfs_node_t cur,
379                                     char *last, char **pathfreep)
380 {
381           size_t len;
382           dirfs_node_t dnp1;
383           char *buf;
384           int count;
385 
386           dbg(5, "called\n");
387 
388           KKASSERT(dmp->dm_root);       /* Sanity check */
389           *pathfreep = NULL;
390           if (cur == NULL)
391                     return NULL;
392           buf = kmalloc(MAXPATHLEN + 1, M_DIRFS_MISC, M_WAITOK);
393 
394           /*
395            * Passed-in trailing element.
396            */
397           count = 0;
398           buf[MAXPATHLEN] = 0;
399           if (last) {
400                     len = strlen(last);
401                     count += len;
402                     if (count <= MAXPATHLEN)
403                               bcopy(last, &buf[MAXPATHLEN - count], len);
404                     ++count;
405                     if (count <= MAXPATHLEN)
406                               buf[MAXPATHLEN - count] = '/';
407           }
408 
409           /*
410            * Iterate through the parents until we hit the root.
411            */
412           dnp1 = cur;
413           while (dirfs_node_isroot(dnp1) == 0) {
414                     count += dnp1->dn_namelen;
415                     if (count <= MAXPATHLEN) {
416                               bcopy(dnp1->dn_name, &buf[MAXPATHLEN - count],
417                                     dnp1->dn_namelen);
418                     }
419                     ++count;
420                     if (count <= MAXPATHLEN)
421                               buf[MAXPATHLEN - count] = '/';
422                     dnp1 = dnp1->dn_parent;
423                     if (dnp1 == NULL)
424                               break;
425           }
426 
427           /*
428            * Prefix with the root mount path.  If the element was unlinked
429            * dnp1 will be NULL and there is no path.
430            */
431           len = strlen(dmp->dm_path);
432           count += len;
433           if (dnp1 && count <= MAXPATHLEN) {
434                     bcopy(dmp->dm_path, &buf[MAXPATHLEN - count], len);
435                     *pathfreep = buf;
436                     dbg(9, "absolute_path %s\n", &buf[MAXPATHLEN - count]);
437                     return (&buf[MAXPATHLEN - count]);
438           } else {
439                     kfree(buf, M_DIRFS_MISC);
440                     *pathfreep = NULL;
441                     return (NULL);
442           }
443 }
444 
445 /*
446  * Return a dirfs_node with a valid descriptor plus an allocated
447  * relative path which can be used in openat(), fstatat(), etc calls
448  * to locate the requested inode.
449  */
450 dirfs_node_t
dirfs_findfd(dirfs_mount_t dmp,dirfs_node_t cur,char ** pathto,char ** pathfreep)451 dirfs_findfd(dirfs_mount_t dmp, dirfs_node_t cur,
452                char **pathto, char **pathfreep)
453 {
454           dirfs_node_t dnp1;
455           int count;
456           char *buf;
457 
458           dbg(5, "called\n");
459 
460           *pathfreep = NULL;
461           *pathto = NULL;
462 
463           if (cur == NULL)
464                     return NULL;
465 
466           buf = kmalloc(MAXPATHLEN + 1, M_DIRFS_MISC, M_WAITOK | M_ZERO);
467           count = 0;
468 
469           dnp1 = cur;
470           while (dnp1 == cur || dnp1->dn_fd == DIRFS_NOFD) {
471                     count += dnp1->dn_namelen;
472                     if (count <= MAXPATHLEN) {
473                               bcopy(dnp1->dn_name, &buf[MAXPATHLEN - count],
474                                     dnp1->dn_namelen);
475                     }
476                     ++count;
477                     if (count <= MAXPATHLEN)
478                               buf[MAXPATHLEN - count] = '/';
479                     dnp1 = dnp1->dn_parent;
480                     KKASSERT(dnp1 != NULL);
481           }
482 
483           if (dnp1 && count <= MAXPATHLEN) {
484                     *pathfreep = buf;
485                     *pathto = &buf[MAXPATHLEN - count + 1]; /* skip '/' prefix */
486                     dirfs_node_ref(dnp1);
487                     dbg(9, "fd=%d dnp1=%p dnp1->dn_name=%d &buf[off]=%s\n",
488                         dnp1->dn_fd, dnp1, dnp1->dn_name, *pathto);
489           } else {
490                     dbg(9, "failed too long\n");
491                     kfree(buf, M_DIRFS_MISC);
492                     *pathfreep = NULL;
493                     *pathto = NULL;
494                     dnp1 = NULL;
495           }
496           return (dnp1);
497 }
498 
499 void
dirfs_dropfd(dirfs_mount_t dmp,dirfs_node_t dnp1,char * pathfree)500 dirfs_dropfd(dirfs_mount_t dmp, dirfs_node_t dnp1, char *pathfree)
501 {
502           if (pathfree)
503                     kfree(pathfree, M_DIRFS_MISC);
504           if (dnp1)
505                     dirfs_node_drop(dmp, dnp1);
506 }
507 
508 int
dirfs_node_getperms(dirfs_node_t dnp,int * flags)509 dirfs_node_getperms(dirfs_node_t dnp, int *flags)
510 {
511           dirfs_mount_t dmp;
512           struct vnode *vp = dnp->dn_vnode;
513           int isowner;
514           int isgroup;
515 
516           /*
517            * There must be an active vnode anyways since that
518            * would indicate the dirfs node has valid data for
519            * for dnp->dn_mode (via lstat syscall).
520            */
521           KKASSERT(vp);
522           dmp = VFS_TO_DIRFS(vp->v_mount);
523 
524           isowner = (dmp->dm_uid == dnp->dn_uid);
525           isgroup = (dmp->dm_gid == dnp->dn_gid);
526 
527           if (isowner) {
528                     if (dnp->dn_mode & S_IRUSR)
529                               *flags |= DIRFS_NODE_RD;
530                     if (dnp->dn_mode & S_IWUSR)
531                               *flags |= DIRFS_NODE_WR;
532                     if (dnp->dn_mode & S_IXUSR)
533                               *flags |= DIRFS_NODE_EXE;
534           } else if (isgroup) {
535                     if (dnp->dn_mode & S_IRGRP)
536                               *flags |= DIRFS_NODE_RD;
537                     if (dnp->dn_mode & S_IWGRP)
538                               *flags |= DIRFS_NODE_WR;
539                     if (dnp->dn_mode & S_IXGRP)
540                               *flags |= DIRFS_NODE_EXE;
541           } else {
542                     if (dnp->dn_mode & S_IROTH)
543                               *flags |= DIRFS_NODE_RD;
544                     if (dnp->dn_mode & S_IWOTH)
545                               *flags |= DIRFS_NODE_WR;
546                     if (dnp->dn_mode & S_IXOTH)
547                               *flags |= DIRFS_NODE_EXE;
548           }
549 
550           return 0;
551 }
552 
553 /*
554  * This requires an allocated node and vnode, otherwise it'll panic
555  */
556 int
dirfs_open_helper(dirfs_mount_t dmp,dirfs_node_t dnp,int parentfd,char * relpath)557 dirfs_open_helper(dirfs_mount_t dmp, dirfs_node_t dnp, int parentfd,
558                       char *relpath)
559 {
560           dirfs_node_t pathnp;
561           char *pathfree;
562           char *tmp;
563           int flags;
564           int perms;
565           int error;
566 
567           dbg(5, "called\n");
568 
569           flags = error = perms = 0;
570           tmp = NULL;
571 
572           KKASSERT(dnp);
573           KKASSERT(dnp->dn_vnode);
574 
575           /*
576            * XXX Besides VDIR and VREG there are other file
577            * types, y'know?
578            * Also, O_RDWR alone might not be the best mode to open
579            * a file with, need to investigate which suits better.
580            */
581           dirfs_node_getperms(dnp, &perms);
582 
583           if (dnp->dn_type & VDIR) {
584                     flags |= O_DIRECTORY;
585           } else {
586                     if (perms & DIRFS_NODE_WR)
587                               flags |= O_RDWR;
588                     else
589                               flags |= O_RDONLY;
590           }
591           if (relpath != NULL) {
592                     tmp = relpath;
593                     pathnp = NULL;
594                     KKASSERT(parentfd != DIRFS_NOFD);
595           } else if (parentfd == DIRFS_NOFD) {
596                     pathnp = dirfs_findfd(dmp, dnp, &tmp, &pathfree);
597                     parentfd = pathnp->dn_fd;
598           } else {
599                     pathnp = NULL;
600           }
601 
602           dnp->dn_fd = openat(parentfd, tmp, flags);
603           if (dnp->dn_fd == -1)
604                     error = errno;
605 
606           dbg(9, "dnp=%p tmp2=%s parentfd=%d flags=%d error=%d "
607               "flags=%08x w=%d x=%d\n", dnp, tmp, parentfd, flags, error,
608               perms);
609 
610           if (pathnp)
611                     dirfs_dropfd(dmp, pathnp, pathfree);
612 
613           return error;
614 }
615 
616 int
dirfs_close_helper(dirfs_node_t dnp)617 dirfs_close_helper(dirfs_node_t dnp)
618 {
619           int error = 0;
620 
621           dbg(5, "called\n");
622 
623 
624           if (dnp->dn_fd != DIRFS_NOFD) {
625                     dbg(9, "closed fd on dnp=%p\n", dnp);
626 #if 0
627                     /* buffer cache buffers may still be present */
628                     error = close(dnp->dn_fd); /* XXX EINTR should be checked */
629                     dnp->dn_fd = DIRFS_NOFD;
630 #endif
631           }
632 
633           return error;
634 }
635 
636 int
dirfs_node_refcnt(dirfs_node_t dnp)637 dirfs_node_refcnt(dirfs_node_t dnp)
638 {
639           return dnp->dn_refcnt;
640 }
641 
642 int
dirfs_node_chtimes(dirfs_node_t dnp)643 dirfs_node_chtimes(dirfs_node_t dnp)
644 {
645           struct vnode *vp;
646           dirfs_mount_t dmp;
647           int error = 0;
648           char *tmp;
649           char *pathfree;
650 
651           vp = NODE_TO_VP(dnp);
652           dmp = VFS_TO_DIRFS(vp->v_mount);
653 
654           KKASSERT(vn_islocked(vp));
655 
656           if (dnp->dn_flags & (IMMUTABLE | APPEND))
657                     return EPERM;
658 
659           tmp = dirfs_node_absolute_path(dmp, dnp, &pathfree);
660           KKASSERT(tmp);
661           if((lutimes(tmp, NULL)) == -1)
662                     error = errno;
663 
664           dirfs_node_stat(DIRFS_NOFD, tmp, dnp);
665           dirfs_dropfd(dmp, NULL, pathfree);
666 
667           KKASSERT(vn_islocked(vp));
668 
669 
670           return error;
671 }
672 
673 int
dirfs_node_chflags(dirfs_node_t dnp,u_long vaflags,struct ucred * cred)674 dirfs_node_chflags(dirfs_node_t dnp, u_long vaflags, struct ucred *cred)
675 {
676           struct vnode *vp;
677           dirfs_mount_t dmp;
678           int error = 0;
679           int flags;
680           char *tmp;
681           char *pathfree;
682 
683           vp = NODE_TO_VP(dnp);
684           dmp = VFS_TO_DIRFS(vp->v_mount);
685 
686           KKASSERT(vn_islocked(vp));
687 
688           flags = dnp->dn_flags;
689 
690           error = vop_helper_setattr_flags(&flags, vaflags, dnp->dn_uid, cred);
691           /*
692            * When running vkernels with non-root it is not possible to set
693            * certain flags on host files, such as SF* flags. chflags(2) call
694            * will spit an error in that case.
695            */
696           if (error == 0) {
697                     tmp = dirfs_node_absolute_path(dmp, dnp, &pathfree);
698                     KKASSERT(tmp);
699                     if((lchflags(tmp, flags)) == -1)
700                               error = errno;
701                     dirfs_node_stat(DIRFS_NOFD, tmp, dnp);
702                     dirfs_dropfd(dmp, NULL, pathfree);
703           }
704 
705           KKASSERT(vn_islocked(vp));
706 
707           return error;
708 }
709 
710 int
dirfs_node_chmod(dirfs_mount_t dmp,dirfs_node_t dnp,mode_t mode)711 dirfs_node_chmod(dirfs_mount_t dmp, dirfs_node_t dnp, mode_t mode)
712 {
713           char *tmp;
714           char *pathfree;
715           int error = 0;
716 
717           tmp = dirfs_node_absolute_path(dmp, dnp, &pathfree);
718           KKASSERT(tmp);
719           if (lchmod(tmp, mode) < 0)
720                     error = errno;
721           dirfs_node_stat(DIRFS_NOFD, tmp, dnp);
722           dirfs_dropfd(dmp, NULL, pathfree);
723 
724           return error;
725 }
726 
727 int
dirfs_node_chown(dirfs_mount_t dmp,dirfs_node_t dnp,uid_t uid,uid_t gid,mode_t mode)728 dirfs_node_chown(dirfs_mount_t dmp, dirfs_node_t dnp,
729                      uid_t uid, uid_t gid, mode_t mode)
730 {
731           char *tmp;
732           char *pathfree;
733           int error = 0;
734 
735           tmp = dirfs_node_absolute_path(dmp, dnp, &pathfree);
736           KKASSERT(tmp);
737           if (lchown(tmp, uid, gid) < 0)
738                     error = errno;
739           if (mode != dnp->dn_mode)
740                     lchmod(tmp, mode);
741           dirfs_node_stat(DIRFS_NOFD, tmp, dnp);
742           dirfs_dropfd(dmp, NULL, pathfree);
743 
744           return error;
745 }
746 
747 
748 int
dirfs_node_chsize(dirfs_node_t dnp,off_t nsize)749 dirfs_node_chsize(dirfs_node_t dnp, off_t nsize)
750 {
751           dirfs_mount_t dmp;
752           struct vnode *vp;
753           int error = 0;
754           char *tmp;
755           char *pathfree;
756           off_t osize;
757           int biosize;
758 
759           KKASSERT(dnp);
760 
761           vp = NODE_TO_VP(dnp);
762           dmp = VFS_TO_DIRFS(vp->v_mount);
763           biosize = BSIZE;
764           osize = dnp->dn_size;
765 
766           KKASSERT(vn_islocked(vp));
767 
768           switch (vp->v_type) {
769           case VDIR:
770                     return (EISDIR);
771           case VREG:
772                     break;
773           default:
774                     return (EOPNOTSUPP);
775 
776           }
777 
778           tmp = dirfs_node_absolute_path(dmp, dnp, &pathfree);
779           if (nsize < osize) {
780                     error = nvtruncbuf(vp, nsize, biosize, -1, 0);
781           } else {
782                     error = nvextendbuf(vp, osize, nsize,
783                                             biosize, biosize,
784                                             -1, -1, 0);
785           }
786           if (error == 0 && truncate(tmp, nsize) < 0)
787                     error = errno;
788           if (error == 0)
789                     dnp->dn_size = nsize;
790           dbg(9, "TRUNCATE %016jx %016jx\n", (intmax_t)nsize, dnp->dn_size);
791           /*dirfs_node_stat(DIRFS_NOFD, tmp, dnp); don't need to do this*/
792 
793           dirfs_dropfd(dmp, NULL, pathfree);
794 
795 
796           KKASSERT(vn_islocked(vp));
797 
798           return error;
799 }
800 
801 void
dirfs_node_setpassive(dirfs_mount_t dmp,dirfs_node_t dnp,int state)802 dirfs_node_setpassive(dirfs_mount_t dmp, dirfs_node_t dnp, int state)
803 {
804           struct vnode *vp;
805 
806           dbg(5, "dnp=%p state=%d dnp->dn_fd=%d\n", dnp, state, dnp->dn_fd);
807 
808           if (state && (dnp->dn_state & DIRFS_PASVFD) == 0 &&
809               dnp->dn_fd != DIRFS_NOFD) {
810                     dirfs_node_ref(dnp);
811                     dirfs_node_setflags(dnp, DIRFS_PASVFD);
812                     TAILQ_INSERT_TAIL(&dmp->dm_fdlist, dnp, dn_fdentry);
813                     ++dirfs_fd_used;
814                     ++dmp->dm_fd_used;
815 
816                     /*
817                      * If we are over our limit remove nodes from the
818                      * passive fd cache.
819                      */
820                     while (dmp->dm_fd_used > dirfs_fd_limit) {
821                               dnp = TAILQ_FIRST(&dmp->dm_fdlist);
822                               dirfs_node_setpassive(dmp, dnp, 0);
823                     }
824           }
825           if (state == 0 && (dnp->dn_state & DIRFS_PASVFD)) {
826                     dirfs_node_clrflags(dnp, DIRFS_PASVFD);
827                     TAILQ_REMOVE(&dmp->dm_fdlist, dnp, dn_fdentry);
828                     --dirfs_fd_used;
829                     --dmp->dm_fd_used;
830                     dbg(5, "dnp=%p removed from fdlist. %d used refs=%d\n",
831                         dnp, dirfs_fd_used, dirfs_node_refcnt(dnp));
832 
833                     /*
834                      * Attempt to close the descriptor.  We can only do this
835                      * if the related vnode is inactive and has exactly two
836                      * refs (representing the vp<->dnp and PASVFD).  Otherwise
837                      * someone might have ref'd the node in order to use the
838                      * dn_fd.
839                      *
840                      * Also, if the vnode is in any way dirty we leave the fd
841                      * open for the buffer cache code.  The syncer will eventually
842                      * come along and fsync the vnode, and the next inactive
843                      * transition will deal with the descriptor.
844                      *
845                      * The descriptor for the root node is NEVER closed by
846                      * this function.
847                      */
848                     vp = dnp->dn_vnode;
849                     if (dirfs_node_refcnt(dnp) == 2 && vp &&
850                         dnp->dn_fd != DIRFS_NOFD &&
851                         !dirfs_node_isroot(dnp) &&
852                         (vp->v_flag & (VINACTIVE|VOBJDIRTY)) == VINACTIVE &&
853                         RB_EMPTY(&vp->v_rbdirty_tree)) {
854                               dbg(9, "passive cache: closing %d\n", dnp->dn_fd);
855                               close(dnp->dn_fd);
856                               dnp->dn_fd = DIRFS_NOFD;
857                     } else {
858                               if (dirfs_node_refcnt(dnp) == 1 && dnp->dn_vnode == NULL &&
859                                   dnp->dn_fd != DIRFS_NOFD &&
860                                   dnp != dmp->dm_root) {
861                                         dbg(9, "passive cache: closing %d\n", dnp->dn_fd);
862                                         close(dnp->dn_fd);
863                                         dnp->dn_fd = DIRFS_NOFD;
864                               }
865                     }
866                     dirfs_node_drop(dmp, dnp);
867           }
868 }
869 
870 char *
dirfs_flag2str(dirfs_node_t dnp)871 dirfs_flag2str(dirfs_node_t dnp)
872 {
873           const char *txtflg[] = { DIRFS_TXTFLG };
874           static char str[512] = {0};
875 
876           if (dnp->dn_state & DIRFS_PASVFD)
877                     ksprintf(str, "%s ", txtflg[0]);
878 
879           return str;
880 }
881 
882 void
debug(int level,const char * fmt,...)883 debug(int level, const char *fmt, ...)
884 {
885           __va_list ap;
886 
887           if (debuglvl >= level) {
888                     __va_start(ap, fmt);
889                     kvprintf(fmt, ap);
890                     __va_end(ap);
891           }
892 }
893