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