2 * Copyright (c) 2000-2001 Boris Popov
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by Boris Popov.
16 * 4. Neither the name of the author nor the names of any co-contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * $FreeBSD: src/sys/fs/smbfs/smbfs_node.c,v 1.2.2.3 2003/01/17 08:20:26 tjr Exp $
33 * $DragonFly: src/sys/vfs/smbfs/smbfs_node.c,v 1.25 2007/08/08 00:12:52 swildner Exp $
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/kernel.h>
40 #include <sys/mount.h>
41 #include <sys/vnode.h>
42 #include <sys/malloc.h>
43 #include <sys/sysctl.h>
45 #include <vm/vm_extern.h>
46 /*#include <vm/vm_page.h>
47 #include <vm/vm_object.h>*/
48 #include <sys/queue.h>
50 #include <netproto/smb/smb.h>
51 #include <netproto/smb/smb_conn.h>
52 #include <netproto/smb/smb_subr.h>
55 #include "smbfs_node.h"
56 #include "smbfs_subr.h"
58 #define SMBFS_NOHASH(smp, hval) (&(smp)->sm_hash[(hval) & (smp)->sm_hashlen])
59 #define smbfs_hash_lock(smp, td) lockmgr(&smp->sm_hashlock, LK_EXCLUSIVE)
60 #define smbfs_hash_unlock(smp, td) lockmgr(&smp->sm_hashlock, LK_RELEASE)
63 MALLOC_DEFINE(M_SMBNODE
, "SMBFS node", "SMBFS vnode private part");
64 static MALLOC_DEFINE(M_SMBNODENAME
, "SMBFS nname", "SMBFS node name");
66 int smbfs_hashprint(struct mount
*mp
);
70 SYSCTL_DECL(_vfs_smbfs
);
72 SYSCTL_PROC(_vfs_smbfs
, OID_AUTO
, vnprint
, CTLFLAG_WR
|CTLTYPE_OPAQUE
,
73 NULL
, 0, smbfs_hashprint
, "S,vnlist", "vnode hash");
76 #define FNV_32_PRIME ((u_int32_t) 0x01000193UL)
77 #define FNV1_32_INIT ((u_int32_t) 33554467UL)
80 smbfs_hash(const u_char
*name
, int nmlen
)
84 for (v
= FNV1_32_INIT
; nmlen
; name
++, nmlen
--) {
86 v
^= (u_int32_t
)*name
;
92 smbfs_hashprint(struct mount
*mp
)
94 struct smbmount
*smp
= VFSTOSMBFS(mp
);
95 struct smbnode_hashhead
*nhpp
;
99 for(i
= 0; i
<= smp
->sm_hashlen
; i
++) {
100 nhpp
= &smp
->sm_hash
[i
];
101 LIST_FOREACH(np
, nhpp
, n_hash
)
102 vprint(NULL
, SMBTOV(np
));
108 smbfs_name_alloc(const u_char
*name
, int nmlen
)
113 #ifdef SMBFS_NAME_DEBUG
114 cp
= kmalloc(nmlen
+ 2 + sizeof(int), M_SMBNODENAME
, M_WAITOK
);
119 bcopy(name
, cp
, nmlen
- 1);
122 cp
= kmalloc(nmlen
, M_SMBNODENAME
, M_WAITOK
);
123 bcopy(name
, cp
, nmlen
- 1);
130 smbfs_name_free(u_char
*name
)
132 #ifdef SMBFS_NAME_DEBUG
139 kprintf("First byte of name entry '%s' corrupted\n", name
);
144 slen
= strlen(name
) + 1;
146 kprintf("Name length mismatch: was %d, now %d name '%s'\n",
150 if (name
[nmlen
] != 0xfe) {
151 kprintf("Last byte of name entry '%s' corrupted\n", name
);
154 kfree(cp
, M_SMBNODENAME
);
156 kfree(name
, M_SMBNODENAME
);
161 smbfs_node_alloc(struct mount
*mp
, struct vnode
*dvp
,
162 const char *name
, int nmlen
, struct smbfattr
*fap
,
165 struct smbmount
*smp
= VFSTOSMBFS(mp
);
166 struct smbnode_hashhead
*nhpp
;
167 struct smbnode
*np
, *np2
, *dnp
;
173 if (smp
->sm_root
!= NULL
&& dvp
== NULL
) {
174 SMBERROR("do not allocate root vnode twice!\n");
177 if (nmlen
== 2 && bcmp(name
, "..", 2) == 0) {
180 vp
= VTOSMB(VTOSMB(dvp
)->n_parent
)->n_vnode
;
181 error
= vget(vp
, LK_EXCLUSIVE
);
185 } else if (nmlen
== 1 && name
[0] == '.') {
186 SMBERROR("do not call me with dot!\n");
189 dnp
= dvp
? VTOSMB(dvp
) : NULL
;
190 if (dnp
== NULL
&& dvp
!= NULL
) {
191 vprint("smbfs_node_alloc: dead parent vnode", dvp
);
194 hashval
= smbfs_hash(name
, nmlen
);
196 smbfs_hash_lock(smp
, td
);
198 nhpp
= SMBFS_NOHASH(smp
, hashval
);
199 LIST_FOREACH(np
, nhpp
, n_hash
) {
200 if (np
->n_parent
!= dvp
||
201 np
->n_nmlen
!= nmlen
|| bcmp(name
, np
->n_name
, nmlen
) != 0)
204 smbfs_hash_unlock(smp
, td
);
205 if (vget(vp
, LK_EXCLUSIVE
) != 0)
208 * relookup after we blocked.
210 LIST_FOREACH(np2
, nhpp
, n_hash
) {
211 if (np2
->n_parent
== dvp
&& np2
->n_nmlen
== nmlen
&&
212 bcmp(name
, np2
->n_name
, nmlen
) == 0)
215 if (np2
!= np
|| SMBTOV(np2
) != vp
) {
222 smbfs_hash_unlock(smp
, td
);
224 * If we don't have node attributes, then it is an explicit lookup
225 * for an existing vnode.
230 MALLOC(np
, struct smbnode
*, sizeof *np
, M_SMBNODE
, M_WAITOK
);
231 error
= getnewvnode(VT_SMBFS
, mp
, &vp
, VLKTIMEOUT
, LK_CANRECURSE
);
236 vp
->v_type
= fap
->fa_attr
& SMB_FA_DIR
? VDIR
: VREG
;
237 bzero(np
, sizeof(*np
));
240 np
->n_mount
= VFSTOSMBFS(mp
);
242 np
->n_name
= smbfs_name_alloc(name
, nmlen
);
243 np
->n_ino
= fap
->fa_ino
;
247 if (/*vp->v_type == VDIR &&*/ (dvp
->v_flag
& VROOT
) == 0) {
249 np
->n_flag
|= NREFPARENT
;
251 } else if (vp
->v_type
== VREG
)
252 SMBERROR("new vnode '%s' born without parent ?\n", np
->n_name
);
254 smbfs_hash_lock(smp
, td
);
255 LIST_FOREACH(np2
, nhpp
, n_hash
) {
256 if (np2
->n_parent
!= dvp
||
257 np2
->n_nmlen
!= nmlen
|| bcmp(name
, np2
->n_name
, nmlen
) != 0)
260 /* smb_name_free(np->n_name);
261 FREE(np, M_SMBNODE);*/
264 LIST_INSERT_HEAD(nhpp
, np
, n_hash
);
265 smbfs_hash_unlock(smp
, td
);
267 * Return a locked and refd vnode
274 smbfs_nget(struct mount
*mp
, struct vnode
*dvp
, const char *name
, int nmlen
,
275 struct smbfattr
*fap
, struct vnode
**vpp
)
282 error
= smbfs_node_alloc(mp
, dvp
, name
, nmlen
, fap
, &vp
);
287 smbfs_attr_cacheenter(vp
, fap
);
293 * Free smbnode, and give vnode back to system
295 * smbfs_reclaim(struct vnode *a_vp)
298 smbfs_reclaim(struct vop_reclaim_args
*ap
)
300 struct vnode
*vp
= ap
->a_vp
;
302 struct smbnode
*np
= VTOSMB(vp
);
303 struct smbmount
*smp
= VTOSMBFS(vp
);
305 SMBVDEBUG("%s,%d\n", np
->n_name
, vp
->v_sysref
.refcnt
);
307 smbfs_hash_lock(smp
, td
);
309 dvp
= (np
->n_parent
&& (np
->n_flag
& NREFPARENT
)) ?
312 if (np
->n_hash
.le_prev
)
313 LIST_REMOVE(np
, n_hash
);
314 if (smp
->sm_root
== np
) {
315 SMBVDEBUG("root vnode\n");
319 smbfs_hash_unlock(smp
, td
);
321 smbfs_name_free(np
->n_name
);
325 * Indicate that we released something; see comment
326 * in smbfs_unmount().
335 * smbfs_inactive(struct vnode *a_vp)
338 smbfs_inactive(struct vop_inactive_args
*ap
)
341 struct vnode
*vp
= ap
->a_vp
;
342 struct smbnode
*np
= VTOSMB(vp
);
343 struct smb_cred scred
;
346 SMBVDEBUG("%s: %d\n", VTOSMB(vp
)->n_name
, vp
->v_sysref
.refcnt
);
347 if (np
->n_opencount
) {
348 error
= smbfs_vinvalbuf(vp
, V_SAVE
, 1);
349 cred
= np
->n_cached_cred
;
350 np
->n_cached_cred
= NULL
;
351 smb_makescred(&scred
, curthread
, cred
);
352 error
= smbfs_smb_close(np
->n_mount
->sm_share
, np
->n_fid
,
353 &np
->n_mtime
, &scred
);
360 * routines to maintain vnode attributes cache
361 * smbfs_attr_cacheenter: unpack np.i to vattr structure
364 smbfs_attr_cacheenter(struct vnode
*vp
, struct smbfattr
*fap
)
366 struct smbnode
*np
= VTOSMB(vp
);
368 if (vp
->v_type
== VREG
) {
369 if (np
->n_size
!= fap
->fa_size
) {
370 np
->n_size
= fap
->fa_size
;
371 vnode_pager_setsize(vp
, np
->n_size
);
373 } else if (vp
->v_type
== VDIR
) {
374 np
->n_size
= 16384; /* should be a better way ... */
377 np
->n_mtime
= fap
->fa_mtime
;
378 np
->n_dosattr
= fap
->fa_attr
;
379 np
->n_attrage
= time_second
;
384 smbfs_attr_cachelookup(struct vnode
*vp
, struct vattr
*va
)
386 struct smbnode
*np
= VTOSMB(vp
);
387 struct smbmount
*smp
= VTOSMBFS(vp
);
390 diff
= time_second
- np
->n_attrage
;
391 if (diff
> 2) /* XXX should be configurable */
393 va
->va_type
= vp
->v_type
; /* vnode type (for create) */
394 if (vp
->v_type
== VREG
) {
395 va
->va_mode
= smp
->sm_args
.file_mode
; /* files access mode and type */
396 } else if (vp
->v_type
== VDIR
) {
397 va
->va_mode
= smp
->sm_args
.dir_mode
; /* files access mode and type */
400 va
->va_size
= np
->n_size
;
401 va
->va_nlink
= 1; /* number of references to file */
402 va
->va_uid
= smp
->sm_args
.uid
; /* owner user id */
403 va
->va_gid
= smp
->sm_args
.gid
; /* owner group id */
404 va
->va_fsid
= vp
->v_mount
->mnt_stat
.f_fsid
.val
[0];
405 va
->va_fileid
= np
->n_ino
; /* file id */
406 if (va
->va_fileid
== 0)
408 va
->va_blocksize
= SSTOVC(smp
->sm_share
)->vc_txmax
;
409 va
->va_mtime
= np
->n_mtime
;
410 va
->va_atime
= va
->va_ctime
= va
->va_mtime
; /* time file changed */
411 va
->va_gen
= VNOVAL
; /* generation number of file */
412 va
->va_flags
= 0; /* flags defined for file */
413 va
->va_rmajor
= VNOVAL
; /* device the special file represents */
414 va
->va_rminor
= VNOVAL
;
415 va
->va_bytes
= va
->va_size
; /* bytes of disk space held by file */
416 va
->va_filerev
= 0; /* file modification number */
417 va
->va_vaflags
= 0; /* operations flags */