sys/kern: Don't implement .vfs_sync unless sync is supported
[dragonfly.git] / sys / vfs / dirfs / dirfs_subr.c
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
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
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
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
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
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
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                                 break;  /* success */
281                         }
282                         vp->v_type = VBAD;
283                         vx_put(vp);
284                         /* multiple dirfs_alloc_vp calls raced, retry */
285                 }
286         }
287         KKASSERT(vp != NULL);
288         *vpp = vp;
289         dbg(9, "dnp=%p vp=%p type=%d\n", dnp, vp, vp->v_type);
290 }
291
292 /*
293  * Do not call locked!
294  */
295 void
296 dirfs_free_vp(dirfs_mount_t dmp, dirfs_node_t dnp)
297 {
298         struct vnode *vp = NODE_TO_VP(dnp);
299
300         dbg(5, "called\n");
301
302         dnp->dn_vnode = NULL;
303         vp->v_data = NULL;
304         dirfs_node_drop(dmp, dnp);
305 }
306
307 int
308 dirfs_nodetype(struct stat *st)
309 {
310         int ret;
311         mode_t mode = st->st_mode;
312
313         if (S_ISDIR(mode))
314                 ret = VDIR;
315         else if (S_ISBLK(mode))
316                 ret = VBLK;
317         else if (S_ISCHR(mode))
318                 ret = VCHR;
319         else if (S_ISFIFO(mode))
320                 ret = VFIFO;
321         else if (S_ISSOCK(mode))
322                 ret = VSOCK;
323         else if (S_ISLNK(mode))
324                 ret = VLNK;
325         else if (S_ISREG(mode))
326                 ret = VREG;
327         else
328                 ret = VBAD;
329
330         return ret;
331 }
332
333 int
334 dirfs_node_stat(int fd, const char *path, dirfs_node_t dnp)
335 {
336         struct stat st;
337         int error;
338
339         dbg(5, "called\n");
340         if (fd == DIRFS_NOFD)
341                 error = lstat(path, &st);
342         else
343                 error = fstatat(fd, path, &st, AT_SYMLINK_NOFOLLOW);
344
345         if (error)
346                 return errno;
347
348         /* Populate our dirfs node struct with stat data */
349         dnp->dn_uid = st.st_uid;
350         dnp->dn_gid = st.st_gid;
351         dnp->dn_mode = st.st_mode;
352         dnp->dn_flags = st.st_flags;
353         dnp->dn_links = st.st_nlink;
354         dnp->dn_atime = st.st_atime;
355         dnp->dn_atimensec = (st.st_atime * 1000000000L);
356         dnp->dn_mtime = st.st_mtime;
357         dnp->dn_mtimensec = (st.st_mtime * 1000000000L);
358         dnp->dn_ctime = st.st_ctime;
359         dnp->dn_ctimensec = (st.st_ctime * 1000000000L);
360         dnp->dn_gen = st.st_gen;
361         dnp->dn_ino = st.st_ino;
362         dnp->dn_st_dev = st.st_dev;
363         dnp->dn_size = st.st_size;
364         dnp->dn_type = dirfs_nodetype(&st);
365
366         return 0;
367 }
368
369 char *
370 dirfs_node_absolute_path(dirfs_mount_t dmp, dirfs_node_t cur, char **pathfreep)
371 {
372         return(dirfs_node_absolute_path_plus(dmp, cur, NULL, pathfreep));
373 }
374
375 char *
376 dirfs_node_absolute_path_plus(dirfs_mount_t dmp, dirfs_node_t cur,
377                               char *last, char **pathfreep)
378 {
379         size_t len;
380         dirfs_node_t dnp1;
381         char *buf;
382         int count;
383
384         dbg(5, "called\n");
385
386         KKASSERT(dmp->dm_root); /* Sanity check */
387         *pathfreep = NULL;
388         if (cur == NULL)
389                 return NULL;
390         buf = kmalloc(MAXPATHLEN + 1, M_DIRFS_MISC, M_WAITOK);
391
392         /*
393          * Passed-in trailing element.
394          */
395         count = 0;
396         buf[MAXPATHLEN] = 0;
397         if (last) {
398                 len = strlen(last);
399                 count += len;
400                 if (count <= MAXPATHLEN)
401                         bcopy(last, &buf[MAXPATHLEN - count], len);
402                 ++count;
403                 if (count <= MAXPATHLEN)
404                         buf[MAXPATHLEN - count] = '/';
405         }
406
407         /*
408          * Iterate through the parents until we hit the root.
409          */
410         dnp1 = cur;
411         while (dirfs_node_isroot(dnp1) == 0) {
412                 count += dnp1->dn_namelen;
413                 if (count <= MAXPATHLEN) {
414                         bcopy(dnp1->dn_name, &buf[MAXPATHLEN - count],
415                               dnp1->dn_namelen);
416                 }
417                 ++count;
418                 if (count <= MAXPATHLEN)
419                         buf[MAXPATHLEN - count] = '/';
420                 dnp1 = dnp1->dn_parent;
421                 if (dnp1 == NULL)
422                         break;
423         }
424
425         /*
426          * Prefix with the root mount path.  If the element was unlinked
427          * dnp1 will be NULL and there is no path.
428          */
429         len = strlen(dmp->dm_path);
430         count += len;
431         if (dnp1 && count <= MAXPATHLEN) {
432                 bcopy(dmp->dm_path, &buf[MAXPATHLEN - count], len);
433                 *pathfreep = buf;
434                 dbg(9, "absolute_path %s\n", &buf[MAXPATHLEN - count]);
435                 return (&buf[MAXPATHLEN - count]);
436         } else {
437                 kfree(buf, M_DIRFS_MISC);
438                 *pathfreep = NULL;
439                 return (NULL);
440         }
441 }
442
443 /*
444  * Return a dirfs_node with a valid descriptor plus an allocated
445  * relative path which can be used in openat(), fstatat(), etc calls
446  * to locate the requested inode.
447  */
448 dirfs_node_t
449 dirfs_findfd(dirfs_mount_t dmp, dirfs_node_t cur,
450              char **pathto, char **pathfreep)
451 {
452         dirfs_node_t dnp1;
453         int count;
454         char *buf;
455
456         dbg(5, "called\n");
457
458         *pathfreep = NULL;
459         *pathto = NULL;
460
461         if (cur == NULL)
462                 return NULL;
463
464         buf = kmalloc(MAXPATHLEN + 1, M_DIRFS_MISC, M_WAITOK | M_ZERO);
465         count = 0;
466
467         dnp1 = cur;
468         while (dnp1 == cur || dnp1->dn_fd == DIRFS_NOFD) {
469                 count += dnp1->dn_namelen;
470                 if (count <= MAXPATHLEN) {
471                         bcopy(dnp1->dn_name, &buf[MAXPATHLEN - count],
472                               dnp1->dn_namelen);
473                 }
474                 ++count;
475                 if (count <= MAXPATHLEN)
476                         buf[MAXPATHLEN - count] = '/';
477                 dnp1 = dnp1->dn_parent;
478                 KKASSERT(dnp1 != NULL);
479         }
480
481         if (dnp1 && count <= MAXPATHLEN) {
482                 *pathfreep = buf;
483                 *pathto = &buf[MAXPATHLEN - count + 1]; /* skip '/' prefix */
484                 dirfs_node_ref(dnp1);
485                 dbg(9, "fd=%d dnp1=%p dnp1->dn_name=%d &buf[off]=%s\n",
486                     dnp1->dn_fd, dnp1, dnp1->dn_name, *pathto);
487         } else {
488                 dbg(9, "failed too long\n");
489                 kfree(buf, M_DIRFS_MISC);
490                 *pathfreep = NULL;
491                 *pathto = NULL;
492                 dnp1 = NULL;
493         }
494         return (dnp1);
495 }
496
497 void
498 dirfs_dropfd(dirfs_mount_t dmp, dirfs_node_t dnp1, char *pathfree)
499 {
500         if (pathfree)
501                 kfree(pathfree, M_DIRFS_MISC);
502         if (dnp1)
503                 dirfs_node_drop(dmp, dnp1);
504 }
505
506 int
507 dirfs_node_getperms(dirfs_node_t dnp, int *flags)
508 {
509         dirfs_mount_t dmp;
510         struct vnode *vp = dnp->dn_vnode;
511         int isowner;
512         int isgroup;
513
514         /*
515          * There must be an active vnode anyways since that
516          * would indicate the dirfs node has valid data for
517          * for dnp->dn_mode (via lstat syscall).
518          */
519         KKASSERT(vp);
520         dmp = VFS_TO_DIRFS(vp->v_mount);
521
522         isowner = (dmp->dm_uid == dnp->dn_uid);
523         isgroup = (dmp->dm_gid == dnp->dn_gid);
524
525         if (isowner) {
526                 if (dnp->dn_mode & S_IRUSR)
527                         *flags |= DIRFS_NODE_RD;
528                 if (dnp->dn_mode & S_IWUSR)
529                         *flags |= DIRFS_NODE_WR;
530                 if (dnp->dn_mode & S_IXUSR)
531                         *flags |= DIRFS_NODE_EXE;
532         } else if (isgroup) {
533                 if (dnp->dn_mode & S_IRGRP)
534                         *flags |= DIRFS_NODE_RD;
535                 if (dnp->dn_mode & S_IWGRP)
536                         *flags |= DIRFS_NODE_WR;
537                 if (dnp->dn_mode & S_IXGRP)
538                         *flags |= DIRFS_NODE_EXE;
539         } else {
540                 if (dnp->dn_mode & S_IROTH)
541                         *flags |= DIRFS_NODE_RD;
542                 if (dnp->dn_mode & S_IWOTH)
543                         *flags |= DIRFS_NODE_WR;
544                 if (dnp->dn_mode & S_IXOTH)
545                         *flags |= DIRFS_NODE_EXE;
546         }
547
548         return 0;
549 }
550
551 /*
552  * This requires an allocated node and vnode, otherwise it'll panic
553  */
554 int
555 dirfs_open_helper(dirfs_mount_t dmp, dirfs_node_t dnp, int parentfd,
556                   char *relpath)
557 {
558         dirfs_node_t pathnp;
559         char *pathfree;
560         char *tmp;
561         int flags;
562         int perms;
563         int error;
564
565         dbg(5, "called\n");
566
567         flags = error = perms = 0;
568         tmp = NULL;
569
570         KKASSERT(dnp);
571         KKASSERT(dnp->dn_vnode);
572
573         /*
574          * XXX Besides VDIR and VREG there are other file
575          * types, y'know?
576          * Also, O_RDWR alone might not be the best mode to open
577          * a file with, need to investigate which suits better.
578          */
579         dirfs_node_getperms(dnp, &perms);
580
581         if (dnp->dn_type & VDIR) {
582                 flags |= O_DIRECTORY;
583         } else {
584                 if (perms & DIRFS_NODE_WR)
585                         flags |= O_RDWR;
586                 else
587                         flags |= O_RDONLY;
588         }
589         if (relpath != NULL) {
590                 tmp = relpath;
591                 pathnp = NULL;
592                 KKASSERT(parentfd != DIRFS_NOFD);
593         } else if (parentfd == DIRFS_NOFD) {
594                 pathnp = dirfs_findfd(dmp, dnp, &tmp, &pathfree);
595                 parentfd = pathnp->dn_fd;
596         } else {
597                 pathnp = NULL;
598         }
599
600         dnp->dn_fd = openat(parentfd, tmp, flags);
601         if (dnp->dn_fd == -1)
602                 error = errno;
603
604         dbg(9, "dnp=%p tmp2=%s parentfd=%d flags=%d error=%d "
605             "flags=%08x w=%d x=%d\n", dnp, tmp, parentfd, flags, error,
606             perms);
607
608         if (pathnp)
609                 dirfs_dropfd(dmp, pathnp, pathfree);
610
611         return error;
612 }
613
614 int
615 dirfs_close_helper(dirfs_node_t dnp)
616 {
617         int error = 0;
618
619         dbg(5, "called\n");
620
621
622         if (dnp->dn_fd != DIRFS_NOFD) {
623                 dbg(9, "closed fd on dnp=%p\n", dnp);
624 #if 0
625                 /* buffer cache buffers may still be present */
626                 error = close(dnp->dn_fd); /* XXX EINTR should be checked */
627                 dnp->dn_fd = DIRFS_NOFD;
628 #endif
629         }
630
631         return error;
632 }
633
634 int
635 dirfs_node_refcnt(dirfs_node_t dnp)
636 {
637         return dnp->dn_refcnt;
638 }
639
640 int
641 dirfs_node_chtimes(dirfs_node_t dnp)
642 {
643         struct vnode *vp;
644         dirfs_mount_t dmp;
645         int error = 0;
646         char *tmp;
647         char *pathfree;
648
649         vp = NODE_TO_VP(dnp);
650         dmp = VFS_TO_DIRFS(vp->v_mount);
651
652         KKASSERT(vn_islocked(vp));
653
654         if (dnp->dn_flags & (IMMUTABLE | APPEND))
655                 return EPERM;
656
657         tmp = dirfs_node_absolute_path(dmp, dnp, &pathfree);
658         KKASSERT(tmp);
659         if((lutimes(tmp, NULL)) == -1)
660                 error = errno;
661
662         dirfs_node_stat(DIRFS_NOFD, tmp, dnp);
663         dirfs_dropfd(dmp, NULL, pathfree);
664
665         KKASSERT(vn_islocked(vp));
666
667
668         return error;
669 }
670
671 int
672 dirfs_node_chflags(dirfs_node_t dnp, int vaflags, struct ucred *cred)
673 {
674         struct vnode *vp;
675         dirfs_mount_t dmp;
676         int error = 0;
677         int flags;
678         char *tmp;
679         char *pathfree;
680
681         vp = NODE_TO_VP(dnp);
682         dmp = VFS_TO_DIRFS(vp->v_mount);
683
684         KKASSERT(vn_islocked(vp));
685
686         flags = dnp->dn_flags;
687
688         error = vop_helper_setattr_flags(&flags, vaflags, dnp->dn_uid, cred);
689         /*
690          * When running vkernels with non-root it is not possible to set
691          * certain flags on host files, such as SF* flags. chflags(2) call
692          * will spit an error in that case.
693          */
694         if (error == 0) {
695                 tmp = dirfs_node_absolute_path(dmp, dnp, &pathfree);
696                 KKASSERT(tmp);
697                 if((lchflags(tmp, flags)) == -1)
698                         error = errno;
699                 dirfs_node_stat(DIRFS_NOFD, tmp, dnp);
700                 dirfs_dropfd(dmp, NULL, pathfree);
701         }
702
703         KKASSERT(vn_islocked(vp));
704
705         return error;
706 }
707
708 int
709 dirfs_node_chmod(dirfs_mount_t dmp, dirfs_node_t dnp, mode_t mode)
710 {
711         char *tmp;
712         char *pathfree;
713         int error = 0;
714
715         tmp = dirfs_node_absolute_path(dmp, dnp, &pathfree);
716         KKASSERT(tmp);
717         if (lchmod(tmp, mode) < 0)
718                 error = errno;
719         dirfs_node_stat(DIRFS_NOFD, tmp, dnp);
720         dirfs_dropfd(dmp, NULL, pathfree);
721
722         return error;
723 }
724
725 int
726 dirfs_node_chown(dirfs_mount_t dmp, dirfs_node_t dnp,
727                  uid_t uid, uid_t gid, mode_t mode)
728 {
729         char *tmp;
730         char *pathfree;
731         int error = 0;
732
733         tmp = dirfs_node_absolute_path(dmp, dnp, &pathfree);
734         KKASSERT(tmp);
735         if (lchown(tmp, uid, gid) < 0)
736                 error = errno;
737         if (mode != dnp->dn_mode)
738                 lchmod(tmp, mode);
739         dirfs_node_stat(DIRFS_NOFD, tmp, dnp);
740         dirfs_dropfd(dmp, NULL, pathfree);
741
742         return error;
743 }
744
745
746 int
747 dirfs_node_chsize(dirfs_node_t dnp, off_t nsize)
748 {
749         dirfs_mount_t dmp;
750         struct vnode *vp;
751         int error = 0;
752         char *tmp;
753         char *pathfree;
754         off_t osize;
755         int biosize;
756
757         KKASSERT(dnp);
758
759         vp = NODE_TO_VP(dnp);
760         dmp = VFS_TO_DIRFS(vp->v_mount);
761         biosize = BSIZE;
762         osize = dnp->dn_size;
763
764         KKASSERT(vn_islocked(vp));
765
766         switch (vp->v_type) {
767         case VDIR:
768                 return (EISDIR);
769         case VREG:
770                 break;
771         default:
772                 return (EOPNOTSUPP);
773
774         }
775
776         tmp = dirfs_node_absolute_path(dmp, dnp, &pathfree);
777         if (nsize < osize) {
778                 error = nvtruncbuf(vp, nsize, biosize, -1, 0);
779         } else {
780                 error = nvextendbuf(vp, osize, nsize,
781                                     biosize, biosize,
782                                     -1, -1, 0);
783         }
784         if (error == 0 && truncate(tmp, nsize) < 0)
785                 error = errno;
786         if (error == 0)
787                 dnp->dn_size = nsize;
788         dbg(9, "TRUNCATE %016jx %016jx\n", (intmax_t)nsize, dnp->dn_size);
789         /*dirfs_node_stat(DIRFS_NOFD, tmp, dnp); don't need to do this*/
790
791         dirfs_dropfd(dmp, NULL, pathfree);
792
793
794         KKASSERT(vn_islocked(vp));
795
796         return error;
797 }
798
799 void
800 dirfs_node_setpassive(dirfs_mount_t dmp, dirfs_node_t dnp, int state)
801 {
802         struct vnode *vp;
803
804         dbg(5, "dnp=%p state=%d dnp->dn_fd=%d\n", dnp, state, dnp->dn_fd);
805
806         if (state && (dnp->dn_state & DIRFS_PASVFD) == 0 &&
807             dnp->dn_fd != DIRFS_NOFD) {
808                 dirfs_node_ref(dnp);
809                 dirfs_node_setflags(dnp, DIRFS_PASVFD);
810                 TAILQ_INSERT_TAIL(&dmp->dm_fdlist, dnp, dn_fdentry);
811                 ++dirfs_fd_used;
812                 ++dmp->dm_fd_used;
813
814                 /*
815                  * If we are over our limit remove nodes from the
816                  * passive fd cache.
817                  */
818                 while (dmp->dm_fd_used > dirfs_fd_limit) {
819                         dnp = TAILQ_FIRST(&dmp->dm_fdlist);
820                         dirfs_node_setpassive(dmp, dnp, 0);
821                 }
822         }
823         if (state == 0 && (dnp->dn_state & DIRFS_PASVFD)) {
824                 dirfs_node_clrflags(dnp, DIRFS_PASVFD);
825                 TAILQ_REMOVE(&dmp->dm_fdlist, dnp, dn_fdentry);
826                 --dirfs_fd_used;
827                 --dmp->dm_fd_used;
828                 dbg(5, "dnp=%p removed from fdlist. %d used refs=%d\n",
829                     dnp, dirfs_fd_used, dirfs_node_refcnt(dnp));
830
831                 /*
832                  * Attempt to close the descriptor.  We can only do this
833                  * if the related vnode is inactive and has exactly two
834                  * refs (representing the vp<->dnp and PASVFD).  Otherwise
835                  * someone might have ref'd the node in order to use the
836                  * dn_fd.
837                  *
838                  * Also, if the vnode is in any way dirty we leave the fd
839                  * open for the buffer cache code.  The syncer will eventually
840                  * come along and fsync the vnode, and the next inactive
841                  * transition will deal with the descriptor.
842                  *
843                  * The descriptor for the root node is NEVER closed by
844                  * this function.
845                  */
846                 vp = dnp->dn_vnode;
847                 if (dirfs_node_refcnt(dnp) == 2 && vp &&
848                     dnp->dn_fd != DIRFS_NOFD &&
849                     !dirfs_node_isroot(dnp) &&
850                     (vp->v_flag & (VINACTIVE|VOBJDIRTY)) == VINACTIVE &&
851                     RB_EMPTY(&vp->v_rbdirty_tree)) {
852                         dbg(9, "passive cache: closing %d\n", dnp->dn_fd);
853                         close(dnp->dn_fd);
854                         dnp->dn_fd = DIRFS_NOFD;
855                 } else {
856                         if (dirfs_node_refcnt(dnp) == 1 && dnp->dn_vnode == NULL &&
857                             dnp->dn_fd != DIRFS_NOFD &&
858                             dnp != dmp->dm_root) {
859                                 dbg(9, "passive cache: closing %d\n", dnp->dn_fd);
860                                 close(dnp->dn_fd);
861                                 dnp->dn_fd = DIRFS_NOFD;
862                         }
863                 }
864                 dirfs_node_drop(dmp, dnp);
865         }
866 }
867
868 char *
869 dirfs_flag2str(dirfs_node_t dnp)
870 {
871         const char *txtflg[] = { DIRFS_TXTFLG };
872         static char str[512] = {0};
873
874         if (dnp->dn_state & DIRFS_PASVFD)
875                 ksprintf(str, "%s ", txtflg[0]);
876
877         return str;
878 }
879
880 void
881 debug(int level, const char *fmt, ...)
882 {
883         __va_list ap;
884
885         if (debuglvl >= level) {
886                 __va_start(ap, fmt);
887                 kvprintf(fmt, ap);
888                 __va_end(ap);
889         }
890 }