1/* $NetBSD: smbfs_node.c,v 1.54 2016/08/20 12:37:08 hannken Exp $ */
2
3/*
4 * Copyright (c) 2000-2001 Boris Popov
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 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by Boris Popov.
18 * 4. Neither the name of the author nor the names of any co-contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 *
34 * FreeBSD: src/sys/fs/smbfs/smbfs_node.c,v 1.5 2001/12/20 22:42:26 dillon Exp
35 */
36
37#include <sys/cdefs.h>
38__KERNEL_RCSID(0, "$NetBSD: smbfs_node.c,v 1.54 2016/08/20 12:37:08 hannken Exp $");
39
40#include <sys/param.h>
41#include <sys/systm.h>
42#include <sys/kernel.h>
43#include <sys/lock.h>
44#include <sys/malloc.h>
45#include <sys/mount.h>
46#include <sys/namei.h>
47#include <sys/proc.h>
48#include <sys/queue.h>
49#include <sys/sysctl.h>
50#include <sys/time.h>
51#include <sys/vnode.h>
52#include <sys/kauth.h>
53
54#include <netsmb/smb.h>
55#include <netsmb/smb_conn.h>
56#include <netsmb/smb_subr.h>
57
58#include <uvm/uvm.h>
59#include <uvm/uvm_extern.h>
60
61#include <fs/smbfs/smbfs.h>
62#include <fs/smbfs/smbfs_node.h>
63#include <fs/smbfs/smbfs_subr.h>
64
65extern int (**smbfs_vnodeop_p)(void *);
66extern int prtactive;
67
68static const struct genfs_ops smbfs_genfsops = {
69 .gop_write = genfs_compat_gop_write,
70};
71
72struct pool smbfs_node_pool;
73
74int
75smbfs_loadvnode(struct mount *mp, struct vnode *vp,
76 const void *key, size_t key_len, const void **new_key)
77{
78 struct smbnode *np;
79
80 np = pool_get(&smbfs_node_pool, PR_WAITOK);
81 memset(np, 0, sizeof(*np));
82
83 vp->v_tag = VT_SMBFS;
84 vp->v_op = smbfs_vnodeop_p;
85 vp->v_type = VNON;
86 vp->v_data = np;
87 genfs_node_init(vp, &smbfs_genfsops);
88
89 mutex_init(&np->n_lock, MUTEX_DEFAULT, IPL_NONE);
90 np->n_key = kmem_alloc(key_len, KM_SLEEP);
91 memcpy(np->n_key, key, key_len);
92 KASSERT(key_len == SMBFS_KEYSIZE(np->n_nmlen));
93 np->n_vnode = vp;
94 np->n_mount = VFSTOSMBFS(mp);
95
96 if (np->n_parent != NULL && (np->n_parent->v_vflag & VV_ROOT) == 0) {
97 vref(np->n_parent);
98 np->n_flag |= NREFPARENT;
99 }
100
101 *new_key = np->n_key;
102
103 return 0;
104}
105
106int
107smbfs_nget(struct mount *mp, struct vnode *dvp, const char *name, int nmlen,
108 struct smbfattr *fap, struct vnode **vpp)
109{
110 struct smbkey *key;
111 struct smbmount *smp __diagused;
112 struct smbnode *np;
113 struct vnode *vp;
114 union {
115 struct smbkey u_key;
116 char u_data[64];
117 } small_key;
118 int error;
119 const int key_len = SMBFS_KEYSIZE(nmlen);
120
121 smp = VFSTOSMBFS(mp);
122
123 /* do not allow allocating root vnode twice */
124 KASSERT(dvp != NULL || smp->sm_root == NULL);
125
126 /* do not call with dot */
127 KASSERT(nmlen != 1 || name[0] != '.');
128
129 if (nmlen == 2 && memcmp(name, "..", 2) == 0) {
130 if (dvp == NULL)
131 return EINVAL;
132 vp = VTOSMB(VTOSMB(dvp)->n_parent)->n_vnode;
133 vref(vp);
134 *vpp = vp;
135 return 0;
136 }
137
138#ifdef DIAGNOSTIC
139 struct smbnode *dnp = dvp ? VTOSMB(dvp) : NULL;
140 if (dnp == NULL && dvp != NULL)
141 panic("smbfs_node_alloc: dead parent vnode %p", dvp);
142#endif
143
144 if (key_len > sizeof(small_key))
145 key = kmem_alloc(key_len, KM_SLEEP);
146 else
147 key = &small_key.u_key;
148 key->k_parent = dvp;
149 key->k_nmlen = nmlen;
150 memcpy(key->k_name, name, nmlen);
151
152retry:
153 error = vcache_get(mp, key, key_len, &vp);
154 if (error)
155 goto out;
156 mutex_enter(vp->v_interlock);
157 np = VTOSMB(vp);
158 KASSERT(np != NULL);
159 mutex_enter(&np->n_lock);
160 mutex_exit(vp->v_interlock);
161
162 if (vp->v_type == VNON) {
163 /*
164 * If we don't have node attributes, then it is an
165 * explicit lookup for an existing vnode.
166 */
167 if (fap == NULL) {
168 mutex_exit(&np->n_lock);
169 vrele(vp);
170 error = ENOENT;
171 goto out;
172 }
173 vp->v_type = fap->fa_attr & SMB_FA_DIR ? VDIR : VREG;
174 np->n_ino = fap->fa_ino;
175 np->n_size = fap->fa_size;
176
177 /* new file vnode has to have a parent */
178 KASSERT(vp->v_type != VREG || dvp != NULL);
179
180 uvm_vnp_setsize(vp, np->n_size);
181 } else {
182 struct vattr vattr;
183
184 /* Force cached attributes to be refreshed if stale. */
185 (void)VOP_GETATTR(vp, &vattr, curlwp->l_cred);
186 /*
187 * If the file type on the server is inconsistent with
188 * what it was when we created the vnode, kill the
189 * bogus vnode now and retry to create a new one with
190 * the right type.
191 */
192 if ((vp->v_type == VDIR && (np->n_dosattr & SMB_FA_DIR) == 0) ||
193 (vp->v_type == VREG && (np->n_dosattr & SMB_FA_DIR) != 0)) {
194 mutex_exit(&np->n_lock);
195 vgone(vp);
196 goto retry;
197 }
198 }
199 if (fap)
200 smbfs_attr_cacheenter(vp, fap);
201 *vpp = vp;
202 mutex_exit(&np->n_lock);
203
204out:
205 if (key != &small_key.u_key)
206 kmem_free(key, key_len);
207 return error;
208}
209
210/*
211 * Free smbnode, and give vnode back to system
212 */
213int
214smbfs_reclaim(void *v)
215{
216 struct vop_reclaim_args /* {
217 struct vnode *a_vp;
218 struct thread *a_p;
219 } */ *ap = v;
220 struct vnode *vp = ap->a_vp;
221 struct vnode *dvp;
222 struct smbnode *np = VTOSMB(vp);
223 struct smbmount *smp = VTOSMBFS(vp);
224
225 if (prtactive && vp->v_usecount > 1)
226 vprint("smbfs_reclaim(): pushing active", vp);
227
228 SMBVDEBUG("%.*s,%d\n", (int) np->n_nmlen, np->n_name, vp->v_usecount);
229
230 dvp = (np->n_parent && (np->n_flag & NREFPARENT)) ?
231 np->n_parent : NULL;
232
233 if (smp->sm_root == np) {
234 SMBVDEBUG0("root vnode\n");
235 smp->sm_root = NULL;
236 }
237
238 genfs_node_destroy(vp);
239
240 /* To interlock with smbfs_nget(). */
241 mutex_enter(vp->v_interlock);
242 vp->v_data = NULL;
243 mutex_exit(vp->v_interlock);
244
245 mutex_destroy(&np->n_lock);
246 kmem_free(np->n_key, SMBFS_KEYSIZE(np->n_nmlen));
247 pool_put(&smbfs_node_pool, np);
248 if (dvp) {
249 vrele(dvp);
250 /*
251 * Indicate that we released something; see comment
252 * in smbfs_unmount().
253 */
254 smp->sm_didrele = 1;
255 }
256 return 0;
257}
258
259int
260smbfs_inactive(void *v)
261{
262 struct vop_inactive_args /* {
263 struct vnode *a_vp;
264 bool *a_recycle;
265 } */ *ap = v;
266 struct lwp *l = curlwp;
267 kauth_cred_t cred = l->l_cred;
268 struct vnode *vp = ap->a_vp;
269 struct smbnode *np = VTOSMB(vp);
270 struct smb_cred scred;
271
272 SMBVDEBUG("%.*s: %d\n", (int) np->n_nmlen, np->n_name, vp->v_usecount);
273 if ((np->n_flag & NOPEN) != 0) {
274 struct smb_share *ssp = np->n_mount->sm_share;
275
276 smbfs_vinvalbuf(vp, V_SAVE, cred, l, 1);
277 smb_makescred(&scred, l, cred);
278
279 if (vp->v_type == VDIR && np->n_dirseq) {
280 smbfs_findclose(np->n_dirseq, &scred);
281 np->n_dirseq = NULL;
282 }
283
284 if (vp->v_type != VDIR
285 || SMB_CAPS(SSTOVC(ssp)) & SMB_CAP_NT_SMBS)
286 smbfs_smb_close(ssp, np->n_fid, &np->n_mtime, &scred);
287
288 np->n_flag &= ~NOPEN;
289 smbfs_attr_cacheremove(vp);
290 }
291 *ap->a_recycle = ((vp->v_type == VNON) || (np->n_flag & NGONE) != 0);
292 VOP_UNLOCK(vp);
293
294 return (0);
295}
296/*
297 * routines to maintain vnode attributes cache
298 * smbfs_attr_cacheenter: unpack np.i to vattr structure
299 */
300void
301smbfs_attr_cacheenter(struct vnode *vp, struct smbfattr *fap)
302{
303 struct smbnode *np = VTOSMB(vp);
304
305 if (vp->v_type == VREG) {
306 if (np->n_size != fap->fa_size) {
307 np->n_size = fap->fa_size;
308 uvm_vnp_setsize(vp, np->n_size);
309 }
310 } else if (vp->v_type == VDIR) {
311 np->n_size = 16384; /* should be a better way ... */
312 } else
313 return;
314
315 np->n_mtime = fap->fa_mtime;
316 np->n_dosattr = fap->fa_attr;
317
318 np->n_attrage = time_uptime;
319}
320
321int
322smbfs_attr_cachelookup(struct vnode *vp, struct vattr *va)
323{
324 struct smbnode *np = VTOSMB(vp);
325 struct smbmount *smp = VTOSMBFS(vp);
326 time_t diff;
327
328 diff = time_uptime - np->n_attrage;
329 if (diff > SMBFS_ATTRTIMO) /* XXX should be configurable */
330 return ENOENT;
331
332 va->va_type = vp->v_type; /* vnode type (for create) */
333 if (vp->v_type == VREG) {
334 va->va_mode = smp->sm_args.file_mode; /* files access mode and type */
335 } else if (vp->v_type == VDIR) {
336 va->va_mode = smp->sm_args.dir_mode; /* files access mode and type */
337 } else
338 return EINVAL;
339 va->va_size = np->n_size;
340 va->va_nlink = 1; /* number of references to file */
341 va->va_uid = smp->sm_args.uid; /* owner user id */
342 va->va_gid = smp->sm_args.gid; /* owner group id */
343 va->va_fsid = vp->v_mount->mnt_stat.f_fsidx.__fsid_val[0];
344 va->va_fileid = np->n_ino; /* file id */
345 if (va->va_fileid == 0)
346 va->va_fileid = 2;
347 va->va_blocksize = SSTOVC(smp->sm_share)->vc_txmax;
348 va->va_mtime = np->n_mtime;
349 va->va_atime = va->va_ctime = va->va_mtime; /* time file changed */
350 va->va_gen = VNOVAL; /* generation number of file */
351 va->va_flags = 0; /* flags defined for file */
352 va->va_rdev = VNOVAL; /* device the special file represents */
353 va->va_bytes = va->va_size; /* bytes of disk space held by file */
354 va->va_filerev = 0; /* file modification number */
355 va->va_vaflags = 0; /* operations flags */
356 return 0;
357}
358