1 /* $NetBSD: smbfs_node.c,v 1.41 2009/07/02 16:17:52 njoly Exp $ */
4 * Copyright (c) 2000-2001 Boris Popov
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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.
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
34 * FreeBSD: src/sys/fs/smbfs/smbfs_node.c,v 1.5 2001/12/20 22:42:26 dillon Exp
37 #include <sys/cdefs.h>
38 __KERNEL_RCSID(0, "$NetBSD: smbfs_node.c,v 1.41 2009/07/02 16:17:52 njoly Exp $");
40 #include <sys/param.h>
41 #include <sys/systm.h>
42 #include <sys/kernel.h>
44 #include <sys/malloc.h>
45 #include <sys/mount.h>
46 #include <sys/namei.h>
48 #include <sys/queue.h>
49 #include <sys/sysctl.h>
51 #include <sys/vnode.h>
52 #include <sys/kauth.h>
54 #include <netsmb/smb.h>
55 #include <netsmb/smb_conn.h>
56 #include <netsmb/smb_subr.h>
59 #include <uvm/uvm_extern.h>
61 #include <fs/smbfs/smbfs.h>
62 #include <fs/smbfs/smbfs_node.h>
63 #include <fs/smbfs/smbfs_subr.h>
65 #define SMBFS_NOHASH(smp, hval) (&(smp)->sm_hash[(hval) & (smp)->sm_hashlen])
67 MALLOC_JUSTDEFINE(M_SMBNODENAME
, "SMBFS nname", "SMBFS node name");
69 extern int (**smbfs_vnodeop_p
)(void *);
72 static const struct genfs_ops smbfs_genfsops
= {
73 .gop_write
= genfs_compat_gop_write
,
76 struct pool smbfs_node_pool
;
79 smbfs_name_alloc(const u_char
*name
, int nmlen
)
83 cp
= malloc(nmlen
, M_SMBNODENAME
, M_WAITOK
);
84 memcpy(cp
, name
, nmlen
);
90 smbfs_name_free(u_char
*name
)
92 free(name
, M_SMBNODENAME
);
96 smbfs_node_alloc(struct mount
*mp
, struct vnode
*dvp
,
97 const char *name
, int nmlen
, struct smbfattr
*fap
, struct vnode
**vpp
)
99 struct smbmount
*smp
= VFSTOSMBFS(mp
);
100 struct smbnode_hashhead
*nhpp
;
101 struct smbnode
*np
, *np2
, *dnp
;
106 /* do not allow allocating root vnode twice */
107 KASSERT(dvp
!= NULL
|| smp
->sm_root
== NULL
);
108 /* do not call with dot */
109 KASSERT(nmlen
!= 1 || name
[0] != '.');
111 if (nmlen
== 2 && memcmp(name
, "..", 2) == 0) {
114 vp
= VTOSMB(VTOSMB(dvp
)->n_parent
)->n_vnode
;
115 if ((error
= vget(vp
, LK_EXCLUSIVE
| LK_RETRY
)) == 0)
120 dnp
= dvp
? VTOSMB(dvp
) : NULL
;
122 if (dnp
== NULL
&& dvp
!= NULL
)
123 panic("smbfs_node_alloc: dead parent vnode %p", dvp
);
125 hashval
= smbfs_hash(name
, nmlen
);
127 mutex_enter(&smp
->sm_hashlock
);
128 nhpp
= SMBFS_NOHASH(smp
, hashval
);
129 LIST_FOREACH(np
, nhpp
, n_hash
) {
130 if (np
->n_parent
!= dvp
131 || np
->n_nmlen
!= nmlen
132 || memcmp(name
, np
->n_name
, nmlen
) != 0)
135 mutex_enter(&(vp
)->v_interlock
);
136 mutex_exit(&smp
->sm_hashlock
);
137 if (vget(vp
, LK_EXCLUSIVE
| LK_INTERLOCK
) != 0)
142 mutex_exit(&smp
->sm_hashlock
);
145 * If we don't have node attributes, then it is an explicit lookup
146 * for an existing vnode.
151 np
= pool_get(&smbfs_node_pool
, PR_WAITOK
);
152 memset(np
, 0, sizeof(*np
));
154 error
= getnewvnode(VT_SMBFS
, mp
, smbfs_vnodeop_p
, &vp
);
156 pool_put(&smbfs_node_pool
, np
);
162 if (/*vp->v_type == VDIR &&*/ (dvp
->v_vflag
& VV_ROOT
) == 0) {
164 np
->n_flag
|= NREFPARENT
;
168 vn_lock(vp
, LK_EXCLUSIVE
| LK_RETRY
);
170 mutex_enter(&smp
->sm_hashlock
);
172 * Check if the vnode wasn't added while we were in getnewvnode/
175 LIST_FOREACH(np2
, nhpp
, n_hash
) {
176 if (np2
->n_parent
!= dvp
177 || np2
->n_nmlen
!= nmlen
178 || memcmp(name
, np2
->n_name
, nmlen
) != 0)
180 mutex_exit(&smp
->sm_hashlock
);
181 pool_put(&smbfs_node_pool
, np
);
183 if ((np
->n_flag
& NREFPARENT
) != 0)
188 vp
->v_type
= fap
->fa_attr
& SMB_FA_DIR
? VDIR
: VREG
;
190 genfs_node_init(vp
, &smbfs_genfsops
);
193 np
->n_mount
= VFSTOSMBFS(mp
);
195 np
->n_name
= smbfs_name_alloc(name
, nmlen
);
196 np
->n_ino
= fap
->fa_ino
;
197 np
->n_size
= fap
->fa_size
;
199 /* new file vnode has to have a parent */
200 KASSERT(vp
->v_type
!= VREG
|| dvp
!= NULL
);
202 /* Not on hash list, add it now */
203 LIST_INSERT_HEAD(nhpp
, np
, n_hash
);
204 uvm_vnp_setsize(vp
, np
->n_size
);
205 mutex_exit(&smp
->sm_hashlock
);
212 smbfs_nget(struct mount
*mp
, struct vnode
*dvp
, const char *name
, int nmlen
,
213 struct smbfattr
*fap
, struct vnode
**vpp
)
218 error
= smbfs_node_alloc(mp
, dvp
, name
, nmlen
, fap
, &vp
);
222 smbfs_attr_cacheenter(vp
, fap
);
228 * Free smbnode, and give vnode back to system
231 smbfs_reclaim(void *v
)
233 struct vop_reclaim_args
/* {
237 struct vnode
*vp
= ap
->a_vp
;
239 struct smbnode
*np
= VTOSMB(vp
);
240 struct smbmount
*smp
= VTOSMBFS(vp
);
242 if (prtactive
&& vp
->v_usecount
> 1)
243 vprint("smbfs_reclaim(): pushing active", vp
);
245 SMBVDEBUG("%.*s,%d\n", (int) np
->n_nmlen
, np
->n_name
, vp
->v_usecount
);
247 mutex_enter(&smp
->sm_hashlock
);
249 dvp
= (np
->n_parent
&& (np
->n_flag
& NREFPARENT
)) ?
252 LIST_REMOVE(np
, n_hash
);
255 if (smp
->sm_root
== np
) {
256 SMBVDEBUG0("root vnode\n");
259 genfs_node_destroy(vp
);
261 mutex_exit(&smp
->sm_hashlock
);
263 smbfs_name_free(np
->n_name
);
264 pool_put(&smbfs_node_pool
, np
);
268 * Indicate that we released something; see comment
269 * in smbfs_unmount().
277 smbfs_inactive(void *v
)
279 struct vop_inactive_args
/* {
283 struct lwp
*l
= curlwp
;
284 kauth_cred_t cred
= l
->l_cred
;
285 struct vnode
*vp
= ap
->a_vp
;
286 struct smbnode
*np
= VTOSMB(vp
);
287 struct smb_cred scred
;
289 SMBVDEBUG("%.*s: %d\n", (int) np
->n_nmlen
, np
->n_name
, vp
->v_usecount
);
290 if ((np
->n_flag
& NOPEN
) != 0) {
291 struct smb_share
*ssp
= np
->n_mount
->sm_share
;
293 smbfs_vinvalbuf(vp
, V_SAVE
, cred
, l
, 1);
294 smb_makescred(&scred
, l
, cred
);
296 if (vp
->v_type
== VDIR
&& np
->n_dirseq
) {
297 smbfs_findclose(np
->n_dirseq
, &scred
);
301 if (vp
->v_type
!= VDIR
302 || SMB_CAPS(SSTOVC(ssp
)) & SMB_CAP_NT_SMBS
)
303 smbfs_smb_close(ssp
, np
->n_fid
, &np
->n_mtime
, &scred
);
305 np
->n_flag
&= ~NOPEN
;
306 smbfs_attr_cacheremove(vp
);
310 *ap
->a_recycle
= false; /* XXX: should set the value properly */
315 * routines to maintain vnode attributes cache
316 * smbfs_attr_cacheenter: unpack np.i to vattr structure
319 smbfs_attr_cacheenter(struct vnode
*vp
, struct smbfattr
*fap
)
321 struct smbnode
*np
= VTOSMB(vp
);
323 if (vp
->v_type
== VREG
) {
324 if (np
->n_size
!= fap
->fa_size
) {
325 np
->n_size
= fap
->fa_size
;
326 uvm_vnp_setsize(vp
, np
->n_size
);
328 } else if (vp
->v_type
== VDIR
) {
329 np
->n_size
= 16384; /* should be a better way ... */
333 np
->n_mtime
= fap
->fa_mtime
;
334 np
->n_dosattr
= fap
->fa_attr
;
336 np
->n_attrage
= time_uptime
;
340 smbfs_attr_cachelookup(struct vnode
*vp
, struct vattr
*va
)
342 struct smbnode
*np
= VTOSMB(vp
);
343 struct smbmount
*smp
= VTOSMBFS(vp
);
346 diff
= time_uptime
- np
->n_attrage
;
347 if (diff
> SMBFS_ATTRTIMO
) /* XXX should be configurable */
350 va
->va_type
= vp
->v_type
; /* vnode type (for create) */
351 if (vp
->v_type
== VREG
) {
352 va
->va_mode
= smp
->sm_args
.file_mode
; /* files access mode and type */
353 } else if (vp
->v_type
== VDIR
) {
354 va
->va_mode
= smp
->sm_args
.dir_mode
; /* files access mode and type */
357 va
->va_size
= np
->n_size
;
358 va
->va_nlink
= 1; /* number of references to file */
359 va
->va_uid
= smp
->sm_args
.uid
; /* owner user id */
360 va
->va_gid
= smp
->sm_args
.gid
; /* owner group id */
361 va
->va_fsid
= vp
->v_mount
->mnt_stat
.f_fsidx
.__fsid_val
[0];
362 va
->va_fileid
= np
->n_ino
; /* file id */
363 if (va
->va_fileid
== 0)
365 va
->va_blocksize
= SSTOVC(smp
->sm_share
)->vc_txmax
;
366 va
->va_mtime
= np
->n_mtime
;
367 va
->va_atime
= va
->va_ctime
= va
->va_mtime
; /* time file changed */
368 va
->va_gen
= VNOVAL
; /* generation number of file */
369 va
->va_flags
= 0; /* flags defined for file */
370 va
->va_rdev
= VNOVAL
; /* device the special file represents */
371 va
->va_bytes
= va
->va_size
; /* bytes of disk space held by file */
372 va
->va_filerev
= 0; /* file modification number */
373 va
->va_vaflags
= 0; /* operations flags */