1 /*        $NetBSD: vfs_cwd.c,v 1.12 2024/12/07 02:11:42 riastradh Exp $         */
2 
3 /*-
4  * Copyright (c) 2008, 2020, 2023 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  * POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 /*
30  * Current working directory.
31  */
32 
33 #include <sys/cdefs.h>
34 __KERNEL_RCSID(0, "$NetBSD: vfs_cwd.c,v 1.12 2024/12/07 02:11:42 riastradh Exp $");
35 
36 #include <sys/param.h>
37 
38 #include <sys/atomic.h>
39 #include <sys/filedesc.h>
40 #include <sys/kmem.h>
41 #include <sys/proc.h>
42 #include <sys/vnode.h>
43 
44 /*
45  * Create an initial cwdinfo structure, using the same current and root
46  * directories as curproc.
47  */
48 struct cwdinfo *
cwdinit(void)49 cwdinit(void)
50 {
51           struct cwdinfo *cwdi;
52           struct cwdinfo *copy;
53 
54           cwdi = kmem_alloc(sizeof(*cwdi), KM_SLEEP);
55           KASSERT(ALIGNED_POINTER(cwdi, COHERENCY_UNIT));
56           rw_init(&cwdi->cwdi_lock);
57           copy = curproc->p_cwdi;
58 
59           rw_enter(&copy->cwdi_lock, RW_READER);
60           cwdi->cwdi_cdir = copy->cwdi_cdir;
61           if (cwdi->cwdi_cdir)
62                     vref(cwdi->cwdi_cdir);
63           cwdi->cwdi_rdir = copy->cwdi_rdir;
64           if (cwdi->cwdi_rdir)
65                     vref(cwdi->cwdi_rdir);
66           cwdi->cwdi_edir = copy->cwdi_edir;
67           if (cwdi->cwdi_edir)
68                     vref(cwdi->cwdi_edir);
69           rw_exit(&copy->cwdi_lock);
70 
71           cwdi->cwdi_cmask = copy->cwdi_cmask;
72           cwdi->cwdi_refcnt = 1;
73 
74           return cwdi;
75 }
76 
77 /*
78  * Make p2 share p1's cwdinfo.
79  */
80 void
cwdshare(struct proc * p2)81 cwdshare(struct proc *p2)
82 {
83           struct cwdinfo *cwdi;
84 
85           cwdi = curproc->p_cwdi;
86 
87           atomic_inc_uint(&cwdi->cwdi_refcnt);
88           p2->p_cwdi = cwdi;
89 }
90 
91 /*
92  * Make sure proc has only one reference to its cwdi, creating
93  * a new one if necessary.
94  */
95 void
cwdunshare(struct proc * p)96 cwdunshare(struct proc *p)
97 {
98           struct cwdinfo *cwdi = p->p_cwdi;
99 
100           if (cwdi->cwdi_refcnt > 1) {
101                     cwdi = cwdinit();
102                     cwdfree(p->p_cwdi);
103                     p->p_cwdi = cwdi;
104           }
105 }
106 
107 /*
108  * Release a cwdinfo structure.
109  */
110 void
cwdfree(struct cwdinfo * cwdi)111 cwdfree(struct cwdinfo *cwdi)
112 {
113 
114           membar_release();
115           if (atomic_dec_uint_nv(&cwdi->cwdi_refcnt) > 0)
116                     return;
117           membar_acquire();
118 
119           vrele(cwdi->cwdi_cdir);
120           rw_destroy(&cwdi->cwdi_lock);
121           if (cwdi->cwdi_rdir)
122                     vrele(cwdi->cwdi_rdir);
123           if (cwdi->cwdi_edir)
124                     vrele(cwdi->cwdi_edir);
125           kmem_free(cwdi, sizeof(*cwdi));
126 }
127 
128 void
cwdexec(struct proc * p)129 cwdexec(struct proc *p)
130 {
131 
132           cwdunshare(p);
133 
134           if (p->p_cwdi->cwdi_edir) {
135                     vrele(p->p_cwdi->cwdi_edir);
136           }
137 }
138