4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 #pragma ident "%Z%%M% %I% %E% SMI"
27
28 #include <sys/types.h>
29 #include <sys/param.h>
30 #include <sys/sysmacros.h>
31 #include <sys/systm.h>
32 #include <sys/time.h>
33 #include <sys/vfs.h>
34 #include <sys/vnode.h>
35 #include <sys/errno.h>
36 #include <sys/cmn_err.h>
37 #include <sys/cred.h>
38 #include <sys/stat.h>
39 #include <sys/debug.h>
40 #include <sys/policy.h>
41 #include <sys/fs/tmpnode.h>
42 #include <sys/fs/tmp.h>
43 #include <sys/vtrace.h>
44
45 static int tdircheckpath(struct tmpnode *, struct tmpnode *, struct cred *);
46 static int tdirrename(struct tmpnode *, struct tmpnode *, struct tmpnode *,
47 char *, struct tmpnode *, struct tdirent *, struct cred *);
508 if (dir->tn_dir->td_prev == tpdp) {
509 dir->tn_dir->td_prev = tpdp->td_prev;
510 }
511 ASSERT(tpdp->td_next != tpdp);
512 ASSERT(tpdp->td_prev != tpdp);
513
514 /*
515 * tpdp points to the correct directory entry
516 */
517 namelen = strlen(tpdp->td_name) + 1;
518
519 tmp_memfree(tpdp, sizeof (struct tdirent) + namelen);
520 dir->tn_size -= (sizeof (struct tdirent) + namelen);
521 dir->tn_dirents--;
522
523 gethrestime(&now);
524 dir->tn_mtime = now;
525 dir->tn_ctime = now;
526 tp->tn_ctime = now;
527
528 ASSERT(tp->tn_nlink > 0);
529 DECR_COUNT(&tp->tn_nlink, &tp->tn_tlock);
530 if (op == DR_RMDIR && tp->tn_type == VDIR) {
531 tdirtrunc(tp);
532 ASSERT(tp->tn_nlink == 0);
533 }
534 return (0);
535 }
536
537 /*
538 * tdirinit is used internally to initialize a directory (dir)
539 * with '.' and '..' entries without checking permissions and locking
540 */
541 void
542 tdirinit(
543 struct tmpnode *parent, /* parent of directory to initialize */
544 struct tmpnode *dir) /* the new directory */
545 {
546 struct tdirent *dot, *dotdot;
547 timestruc_t now;
548
549 ASSERT(RW_WRITE_HELD(&parent->tn_rwlock));
550 ASSERT(dir->tn_type == VDIR);
551
552 dot = tmp_memalloc(sizeof (struct tdirent) + 2, TMP_MUSTHAVE);
553 dotdot = tmp_memalloc(sizeof (struct tdirent) + 3, TMP_MUSTHAVE);
598 }
599
600 dir->tn_dir = dot;
601 dir->tn_size = 2 * sizeof (struct tdirent) + 5; /* dot and dotdot */
602 dir->tn_dirents = 2;
603 dir->tn_nlink = 2;
604 }
605
606
607 /*
608 * tdirtrunc is called to remove all directory entries under this directory.
609 */
610 void
611 tdirtrunc(struct tmpnode *dir)
612 {
613 struct tdirent *tdp;
614 struct tmpnode *tp;
615 size_t namelen;
616 timestruc_t now;
617 int isvattrdir, isdotdot, skip_decr;
618
619 ASSERT(RW_WRITE_HELD(&dir->tn_rwlock));
620 ASSERT(dir->tn_type == VDIR);
621
622 isvattrdir = (dir->tn_vnode->v_flag & V_XATTRDIR) ? 1 : 0;
623 for (tdp = dir->tn_dir; tdp; tdp = dir->tn_dir) {
624 ASSERT(tdp->td_next != tdp);
625 ASSERT(tdp->td_prev != tdp);
626 ASSERT(tdp->td_tmpnode);
627
628 dir->tn_dir = tdp->td_next;
629 namelen = strlen(tdp->td_name) + 1;
630
631 /*
632 * Adjust the link counts to account for this directory
633 * entry removal. Hidden attribute directories may
634 * not be empty as they may be truncated as a side-
635 * effect of removing the parent. We do hold/rele
636 * operations to free up these tmpnodes.
637 *
638 * Skip the link count adjustment for parents of
639 * attribute directories as those link counts
640 * do not include the ".." reference in the hidden
641 * directories.
642 */
643 tp = tdp->td_tmpnode;
644 isdotdot = (strcmp("..", tdp->td_name) == 0);
645 skip_decr = (isvattrdir && isdotdot);
646 if (!skip_decr) {
647 ASSERT(tp->tn_nlink > 0);
648 DECR_COUNT(&tp->tn_nlink, &tp->tn_tlock);
649 }
650
651 tmpfs_hash_out(tdp);
652
653 tmp_memfree(tdp, sizeof (struct tdirent) + namelen);
654 dir->tn_size -= (sizeof (struct tdirent) + namelen);
655 dir->tn_dirents--;
656 }
657
658 gethrestime(&now);
659 dir->tn_mtime = now;
660 dir->tn_ctime = now;
661
662 ASSERT(dir->tn_dir == NULL);
663 ASSERT(dir->tn_size == 0);
664 ASSERT(dir->tn_dirents == 0);
665 }
666
667 /*
668 * Check if the source directory is in the path of the target directory.
669 * The target directory is locked by the caller.
670 *
671 * XXX - The source and target's should be different upon entry.
672 */
673 static int
674 tdircheckpath(
675 struct tmpnode *fromtp,
676 struct tmpnode *toparent,
|
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 *
25 * Copyright 2016 RackTop Systems.
26 */
27
28 #include <sys/types.h>
29 #include <sys/param.h>
30 #include <sys/sysmacros.h>
31 #include <sys/systm.h>
32 #include <sys/time.h>
33 #include <sys/vfs.h>
34 #include <sys/vnode.h>
35 #include <sys/errno.h>
36 #include <sys/cmn_err.h>
37 #include <sys/cred.h>
38 #include <sys/stat.h>
39 #include <sys/debug.h>
40 #include <sys/policy.h>
41 #include <sys/fs/tmpnode.h>
42 #include <sys/fs/tmp.h>
43 #include <sys/vtrace.h>
44
45 static int tdircheckpath(struct tmpnode *, struct tmpnode *, struct cred *);
46 static int tdirrename(struct tmpnode *, struct tmpnode *, struct tmpnode *,
47 char *, struct tmpnode *, struct tdirent *, struct cred *);
508 if (dir->tn_dir->td_prev == tpdp) {
509 dir->tn_dir->td_prev = tpdp->td_prev;
510 }
511 ASSERT(tpdp->td_next != tpdp);
512 ASSERT(tpdp->td_prev != tpdp);
513
514 /*
515 * tpdp points to the correct directory entry
516 */
517 namelen = strlen(tpdp->td_name) + 1;
518
519 tmp_memfree(tpdp, sizeof (struct tdirent) + namelen);
520 dir->tn_size -= (sizeof (struct tdirent) + namelen);
521 dir->tn_dirents--;
522
523 gethrestime(&now);
524 dir->tn_mtime = now;
525 dir->tn_ctime = now;
526 tp->tn_ctime = now;
527
528 /*
529 * If this is a _REMOVE (unlink) operation there may
530 * be other links to the directory entry.
531 */
532 ASSERT(tp->tn_nlink > 0);
533 DECR_COUNT(&tp->tn_nlink, &tp->tn_tlock);
534 if (op == DR_RMDIR || (op == DR_REMOVE && tp->tn_type == VDIR)) {
535 if (tp->tn_nlink > 1) {
536 ASSERT(op == DR_REMOVE);
537 } else {
538 tdirtrunc(tp);
539 ASSERT(tp->tn_nlink == 0);
540 }
541 }
542 return (0);
543 }
544
545 /*
546 * tdirinit is used internally to initialize a directory (dir)
547 * with '.' and '..' entries without checking permissions and locking
548 */
549 void
550 tdirinit(
551 struct tmpnode *parent, /* parent of directory to initialize */
552 struct tmpnode *dir) /* the new directory */
553 {
554 struct tdirent *dot, *dotdot;
555 timestruc_t now;
556
557 ASSERT(RW_WRITE_HELD(&parent->tn_rwlock));
558 ASSERT(dir->tn_type == VDIR);
559
560 dot = tmp_memalloc(sizeof (struct tdirent) + 2, TMP_MUSTHAVE);
561 dotdot = tmp_memalloc(sizeof (struct tdirent) + 3, TMP_MUSTHAVE);
606 }
607
608 dir->tn_dir = dot;
609 dir->tn_size = 2 * sizeof (struct tdirent) + 5; /* dot and dotdot */
610 dir->tn_dirents = 2;
611 dir->tn_nlink = 2;
612 }
613
614
615 /*
616 * tdirtrunc is called to remove all directory entries under this directory.
617 */
618 void
619 tdirtrunc(struct tmpnode *dir)
620 {
621 struct tdirent *tdp;
622 struct tmpnode *tp;
623 size_t namelen;
624 timestruc_t now;
625 int isvattrdir, isdotdot, skip_decr;
626 int lock_held;
627
628 ASSERT(RW_WRITE_HELD(&dir->tn_rwlock));
629 ASSERT(dir->tn_type == VDIR);
630
631 isvattrdir = (dir->tn_vnode->v_flag & V_XATTRDIR) ? 1 : 0;
632 for (tdp = dir->tn_dir; tdp; tdp = dir->tn_dir) {
633 ASSERT(tdp->td_next != tdp);
634 ASSERT(tdp->td_prev != tdp);
635 ASSERT(tdp->td_tmpnode);
636
637 dir->tn_dir = tdp->td_next;
638 namelen = strlen(tdp->td_name) + 1;
639
640 /*
641 * Adjust the link counts to account for this directory
642 * entry removal. Hidden attribute directories may
643 * not be empty as they may be truncated as a side-
644 * effect of removing the parent. We do hold/rele
645 * operations to free up these tmpnodes.
646 *
647 * Skip the link count adjustment for parents of
648 * attribute directories as those link counts
649 * do not include the ".." reference in the hidden
650 * directories.
651 */
652 tp = tdp->td_tmpnode;
653 isdotdot = (strcmp("..", tdp->td_name) == 0);
654 skip_decr = (isvattrdir && isdotdot);
655 if (!skip_decr) {
656 ASSERT(tp->tn_nlink > 0);
657 DECR_COUNT(&tp->tn_nlink, &tp->tn_tlock);
658 }
659
660 tmpfs_hash_out(tdp);
661
662 tmp_memfree(tdp, sizeof (struct tdirent) + namelen);
663 dir->tn_size -= (sizeof (struct tdirent) + namelen);
664 dir->tn_dirents--;
665
666 /*
667 * This directory entry may itself be a directory with
668 * entries and removing it may have created orphans.
669 * On a normal filesystem like UFS this wouldn't be
670 * a huge problem because fcsk can reclaim them. For
671 * TMPFS which resides in RAM however, it means we
672 * end up leaking memory.
673 *
674 * To avoid this we also truncate child directories,
675 * but only if they have no other links to them.
676 */
677 if (!isdotdot && tp->tn_type == VDIR && tp != dir) {
678 if (tp->tn_nlink > 1)
679 continue;
680 lock_held = RW_WRITE_HELD(&tp->tn_rwlock);
681 if (!lock_held)
682 rw_enter(&tp->tn_rwlock, RW_WRITER);
683 tdirtrunc(tp);
684 if (!lock_held)
685 rw_exit(&tp->tn_rwlock);
686 ASSERT(tp->tn_nlink == 0);
687 }
688 }
689
690 gethrestime(&now);
691 dir->tn_mtime = now;
692 dir->tn_ctime = now;
693
694 ASSERT(dir->tn_dir == NULL);
695 ASSERT(dir->tn_size == 0);
696 ASSERT(dir->tn_dirents == 0);
697 }
698
699 /*
700 * Check if the source directory is in the path of the target directory.
701 * The target directory is locked by the caller.
702 *
703 * XXX - The source and target's should be different upon entry.
704 */
705 static int
706 tdircheckpath(
707 struct tmpnode *fromtp,
708 struct tmpnode *toparent,
|