Commit | Line | Data |
---|---|---|
984263bc MD |
1 | /* |
2 | * Copyright (c) 2000-2001, Boris Popov | |
3 | * All rights reserved. | |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions | |
7 | * are met: | |
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. | |
19 | * | |
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 | |
30 | * SUCH DAMAGE. | |
31 | * | |
32 | * $FreeBSD: src/sys/fs/smbfs/smbfs_vfsops.c,v 1.2.2.5 2003/01/17 08:20:26 tjr Exp $ | |
3c37c940 | 33 | * $DragonFly: src/sys/vfs/smbfs/smbfs_vfsops.c,v 1.33 2007/05/06 19:23:35 dillon Exp $ |
984263bc MD |
34 | */ |
35 | #include "opt_netsmb.h" | |
36 | #ifndef NETSMB | |
37 | #error "SMBFS requires option NETSMB" | |
38 | #endif | |
39 | ||
40 | #include <sys/param.h> | |
41 | #include <sys/systm.h> | |
42 | #include <sys/proc.h> | |
43 | #include <sys/kernel.h> | |
44 | #include <sys/sysctl.h> | |
45 | #include <sys/vnode.h> | |
46 | #include <sys/mount.h> | |
47 | #include <sys/stat.h> | |
48 | #include <sys/malloc.h> | |
49 | #include <sys/module.h> | |
50 | ||
51 | ||
d2438d69 MD |
52 | #include <netproto/smb/smb.h> |
53 | #include <netproto/smb/smb_conn.h> | |
54 | #include <netproto/smb/smb_subr.h> | |
55 | #include <netproto/smb/smb_dev.h> | |
984263bc | 56 | |
1f2de5d4 MD |
57 | #include "smbfs.h" |
58 | #include "smbfs_node.h" | |
59 | #include "smbfs_subr.h" | |
984263bc MD |
60 | |
61 | #include <sys/buf.h> | |
62 | ||
66a1ddf5 | 63 | extern struct vop_ops smbfs_vnode_vops; |
0961aa92 | 64 | |
984263bc MD |
65 | int smbfs_debuglevel = 0; |
66 | ||
67 | static int smbfs_version = SMBFS_VERSION; | |
68 | ||
69 | #ifdef SMBFS_USEZONE | |
70 | #include <vm/vm.h> | |
71 | #include <vm/vm_extern.h> | |
72 | #include <vm/vm_zone.h> | |
73 | ||
74 | vm_zone_t smbfsmount_zone; | |
75 | #endif | |
76 | ||
77 | SYSCTL_NODE(_vfs, OID_AUTO, smbfs, CTLFLAG_RW, 0, "SMB/CIFS file system"); | |
78 | SYSCTL_INT(_vfs_smbfs, OID_AUTO, version, CTLFLAG_RD, &smbfs_version, 0, ""); | |
79 | SYSCTL_INT(_vfs_smbfs, OID_AUTO, debuglevel, CTLFLAG_RW, &smbfs_debuglevel, 0, ""); | |
80 | ||
81 | static MALLOC_DEFINE(M_SMBFSHASH, "SMBFS hash", "SMBFS hash table"); | |
82 | ||
83 | ||
acde96db | 84 | static int smbfs_mount(struct mount *, char *, caddr_t, struct ucred *); |
984263bc | 85 | static int smbfs_root(struct mount *, struct vnode **); |
acde96db | 86 | static int smbfs_statfs(struct mount *, struct statfs *, struct ucred *); |
87de5057 | 87 | static int smbfs_sync(struct mount *, int); |
acde96db | 88 | static int smbfs_unmount(struct mount *, int); |
984263bc MD |
89 | static int smbfs_init(struct vfsconf *vfsp); |
90 | static int smbfs_uninit(struct vfsconf *vfsp); | |
91 | ||
84c73a71 | 92 | #if defined(__FreeBSD__) && __FreeBSD_version < 400009 |
984263bc MD |
93 | static int smbfs_vget(struct mount *mp, ino_t ino, struct vnode **vpp); |
94 | static int smbfs_fhtovp(struct mount *, struct fid *, | |
95 | struct sockaddr *, struct vnode **, int *, | |
96 | struct ucred **); | |
97 | static int smbfs_vptofh(struct vnode *, struct fid *); | |
98 | #endif | |
99 | ||
100 | static struct vfsops smbfs_vfsops = { | |
43c45e8f HP |
101 | .vfs_mount = smbfs_mount, |
102 | .vfs_unmount = smbfs_unmount, | |
103 | .vfs_root = smbfs_root, | |
104 | .vfs_statfs = smbfs_statfs, | |
105 | .vfs_sync = smbfs_sync, | |
106 | .vfs_init = smbfs_init, | |
107 | .vfs_uninit = smbfs_uninit | |
984263bc MD |
108 | }; |
109 | ||
110 | ||
111 | VFS_SET(smbfs_vfsops, smbfs, VFCF_NETWORK); | |
112 | ||
113 | MODULE_DEPEND(smbfs, netsmb, NSMB_VERSION, NSMB_VERSION, NSMB_VERSION); | |
114 | MODULE_DEPEND(smbfs, libiconv, 1, 1, 1); | |
115 | MODULE_DEPEND(smbfs, libmchain, 1, 1, 1); | |
116 | ||
117 | int smbfs_pbuf_freecnt = -1; /* start out unlimited */ | |
118 | ||
119 | static int | |
acde96db | 120 | smbfs_mount(struct mount *mp, char *path, caddr_t data, struct ucred *cred) |
984263bc MD |
121 | { |
122 | struct smbfs_args args; /* will hold data from mount request */ | |
123 | struct smbmount *smp = NULL; | |
124 | struct smb_vc *vcp; | |
125 | struct smb_share *ssp = NULL; | |
126 | struct vnode *vp; | |
127 | struct smb_cred scred; | |
984263bc MD |
128 | int error; |
129 | char *pc, *pe; | |
130 | ||
131 | if (data == NULL) { | |
086c1d7e | 132 | kprintf("missing data argument\n"); |
984263bc MD |
133 | return EINVAL; |
134 | } | |
135 | if (mp->mnt_flag & MNT_UPDATE) { | |
086c1d7e | 136 | kprintf("MNT_UPDATE not implemented"); |
984263bc MD |
137 | return EOPNOTSUPP; |
138 | } | |
139 | error = copyin(data, (caddr_t)&args, sizeof(struct smbfs_args)); | |
140 | if (error) | |
141 | return error; | |
142 | if (args.version != SMBFS_VERSION) { | |
086c1d7e | 143 | kprintf("mount version mismatch: kernel=%d, mount=%d\n", |
984263bc MD |
144 | SMBFS_VERSION, args.version); |
145 | return EINVAL; | |
146 | } | |
acde96db | 147 | smb_makescred(&scred, curthread, cred); |
984263bc MD |
148 | error = smb_dev2share(args.dev, SMBM_EXEC, &scred, &ssp); |
149 | if (error) { | |
086c1d7e | 150 | kprintf("invalid device handle %d (%d)\n", args.dev, error); |
984263bc MD |
151 | return error; |
152 | } | |
153 | vcp = SSTOVC(ssp); | |
fef8985e | 154 | smb_share_unlock(ssp, 0); |
984263bc MD |
155 | mp->mnt_stat.f_iosize = SSTOVC(ssp)->vc_txmax; |
156 | ||
157 | #ifdef SMBFS_USEZONE | |
158 | smp = zalloc(smbfsmount_zone); | |
159 | #else | |
dc1fd4b3 | 160 | MALLOC(smp, struct smbmount*, sizeof(*smp), M_SMBFSDATA, M_WAITOK|M_USE_RESERVE); |
984263bc MD |
161 | #endif |
162 | if (smp == NULL) { | |
086c1d7e | 163 | kprintf("could not alloc smbmount\n"); |
984263bc MD |
164 | error = ENOMEM; |
165 | goto bad; | |
166 | } | |
167 | bzero(smp, sizeof(*smp)); | |
168 | mp->mnt_data = (qaddr_t)smp; | |
acde96db | 169 | smp->sm_cred = crhold(cred); |
984263bc MD |
170 | smp->sm_hash = hashinit(desiredvnodes, M_SMBFSHASH, &smp->sm_hashlen); |
171 | if (smp->sm_hash == NULL) | |
172 | goto bad; | |
f2770c70 | 173 | lockinit(&smp->sm_hashlock, "smbfsh", 0, 0); |
984263bc MD |
174 | smp->sm_share = ssp; |
175 | smp->sm_root = NULL; | |
176 | smp->sm_args = args; | |
177 | smp->sm_caseopt = args.caseopt; | |
178 | smp->sm_args.file_mode = (smp->sm_args.file_mode & | |
179 | (S_IRWXU|S_IRWXG|S_IRWXO)) | S_IFREG; | |
180 | smp->sm_args.dir_mode = (smp->sm_args.dir_mode & | |
181 | (S_IRWXU|S_IRWXG|S_IRWXO)) | S_IFDIR; | |
182 | ||
183 | /* simple_lock_init(&smp->sm_npslock);*/ | |
984263bc MD |
184 | pc = mp->mnt_stat.f_mntfromname; |
185 | pe = pc + sizeof(mp->mnt_stat.f_mntfromname); | |
186 | bzero(pc, MNAMELEN); | |
187 | *pc++ = '/'; | |
188 | *pc++ = '/'; | |
189 | pc=index(strncpy(pc, vcp->vc_username, pe - pc - 2), 0); | |
190 | if (pc < pe-1) { | |
191 | *(pc++) = '@'; | |
192 | pc = index(strncpy(pc, vcp->vc_srvname, pe - pc - 2), 0); | |
193 | if (pc < pe - 1) { | |
194 | *(pc++) = '/'; | |
195 | strncpy(pc, ssp->ss_name, pe - pc - 2); | |
196 | } | |
197 | } | |
198 | /* protect against invalid mount points */ | |
199 | smp->sm_args.mount_point[sizeof(smp->sm_args.mount_point) - 1] = '\0'; | |
200 | vfs_getnewfsid(mp); | |
0961aa92 | 201 | |
66a1ddf5 | 202 | vfs_add_vnodeops(mp, &smbfs_vnode_vops, &mp->mnt_vn_norm_ops); |
0961aa92 | 203 | |
984263bc MD |
204 | error = smbfs_root(mp, &vp); |
205 | if (error) | |
206 | goto bad; | |
a11aaa81 | 207 | vn_unlock(vp); |
3c37c940 | 208 | SMBVDEBUG("root.v_sysrefs = %d\n", vp->v_sysref.refcnt); |
984263bc MD |
209 | |
210 | #ifdef DIAGNOSTICS | |
211 | SMBERROR("mp=%p\n", mp); | |
212 | #endif | |
213 | return error; | |
214 | bad: | |
215 | if (smp) { | |
acde96db MD |
216 | if (smp->sm_cred) |
217 | crfree(smp->sm_cred); | |
984263bc | 218 | if (smp->sm_hash) |
efda3bd0 | 219 | kfree(smp->sm_hash, M_SMBFSHASH); |
984263bc MD |
220 | lockdestroy(&smp->sm_hashlock); |
221 | #ifdef SMBFS_USEZONE | |
222 | zfree(smbfsmount_zone, smp); | |
223 | #else | |
efda3bd0 | 224 | kfree(smp, M_SMBFSDATA); |
984263bc MD |
225 | #endif |
226 | } | |
227 | if (ssp) | |
228 | smb_share_put(ssp, &scred); | |
229 | return error; | |
230 | } | |
231 | ||
232 | /* Unmount the filesystem described by mp. */ | |
233 | static int | |
acde96db | 234 | smbfs_unmount(struct mount *mp, int mntflags) |
984263bc MD |
235 | { |
236 | struct smbmount *smp = VFSTOSMBFS(mp); | |
237 | struct smb_cred scred; | |
238 | int error, flags; | |
239 | ||
240 | SMBVDEBUG("smbfs_unmount: flags=%04x\n", mntflags); | |
241 | flags = 0; | |
242 | if (mntflags & MNT_FORCE) | |
243 | flags |= FORCECLOSE; | |
244 | /* | |
245 | * Keep trying to flush the vnode list for the mount while | |
246 | * some are still busy and we are making progress towards | |
247 | * making them not busy. This is needed because smbfs vnodes | |
248 | * reference their parent directory but may appear after their | |
249 | * parent in the list; one pass over the vnode list is not | |
250 | * sufficient in this case. | |
251 | */ | |
252 | do { | |
253 | smp->sm_didrele = 0; | |
254 | /* There is 1 extra root vnode reference from smbfs_mount(). */ | |
255 | error = vflush(mp, 1, flags); | |
256 | } while (error == EBUSY && smp->sm_didrele != 0); | |
257 | if (error) | |
258 | return error; | |
acde96db | 259 | smb_makescred(&scred, curthread, smp->sm_cred); |
984263bc MD |
260 | smb_share_put(smp->sm_share, &scred); |
261 | mp->mnt_data = (qaddr_t)0; | |
262 | ||
acde96db MD |
263 | if (smp->sm_cred) |
264 | crfree(smp->sm_cred); | |
984263bc | 265 | if (smp->sm_hash) |
efda3bd0 | 266 | kfree(smp->sm_hash, M_SMBFSHASH); |
984263bc MD |
267 | lockdestroy(&smp->sm_hashlock); |
268 | #ifdef SMBFS_USEZONE | |
269 | zfree(smbfsmount_zone, smp); | |
270 | #else | |
efda3bd0 | 271 | kfree(smp, M_SMBFSDATA); |
984263bc MD |
272 | #endif |
273 | mp->mnt_flag &= ~MNT_LOCAL; | |
274 | return error; | |
275 | } | |
276 | ||
277 | /* | |
278 | * Return locked root vnode of a filesystem | |
279 | */ | |
280 | static int | |
281 | smbfs_root(struct mount *mp, struct vnode **vpp) | |
282 | { | |
dadab5e9 | 283 | struct thread *td = curthread; /* XXX */ |
984263bc MD |
284 | struct smbmount *smp = VFSTOSMBFS(mp); |
285 | struct vnode *vp; | |
286 | struct smbnode *np; | |
287 | struct smbfattr fattr; | |
dadab5e9 | 288 | struct ucred *cred; |
984263bc MD |
289 | struct smb_cred scred; |
290 | int error; | |
291 | ||
292 | if (smp == NULL) { | |
293 | SMBERROR("smp == NULL (bug in umount)\n"); | |
294 | return EINVAL; | |
295 | } | |
296 | if (smp->sm_root) { | |
297 | *vpp = SMBTOV(smp->sm_root); | |
87de5057 | 298 | return vget(*vpp, LK_EXCLUSIVE | LK_RETRY); |
984263bc | 299 | } |
61670a01 MD |
300 | if (td->td_proc) |
301 | cred = td->td_proc->p_ucred; | |
302 | else | |
303 | cred = proc0.p_ucred; | |
304 | ||
dadab5e9 | 305 | smb_makescred(&scred, td, cred); |
984263bc MD |
306 | error = smbfs_smb_lookup(NULL, NULL, 0, &fattr, &scred); |
307 | if (error) | |
308 | return error; | |
309 | error = smbfs_nget(mp, NULL, "TheRooT", 7, &fattr, &vp); | |
310 | if (error) | |
311 | return error; | |
312 | vp->v_flag |= VROOT; | |
313 | np = VTOSMB(vp); | |
314 | smp->sm_root = np; | |
315 | *vpp = vp; | |
316 | return 0; | |
317 | } | |
318 | ||
984263bc MD |
319 | /*ARGSUSED*/ |
320 | int | |
321 | smbfs_init(struct vfsconf *vfsp) | |
322 | { | |
984263bc MD |
323 | #ifdef SMBFS_USEZONE |
324 | smbfsmount_zone = zinit("SMBFSMOUNT", sizeof(struct smbmount), 0, 0, 1); | |
325 | #endif | |
326 | smbfs_pbuf_freecnt = nswbuf / 2 + 1; | |
327 | SMBVDEBUG("done.\n"); | |
328 | return 0; | |
329 | } | |
330 | ||
331 | /*ARGSUSED*/ | |
332 | int | |
333 | smbfs_uninit(struct vfsconf *vfsp) | |
334 | { | |
984263bc MD |
335 | SMBVDEBUG("done.\n"); |
336 | return 0; | |
337 | } | |
338 | ||
339 | /* | |
340 | * smbfs_statfs call | |
341 | */ | |
342 | int | |
acde96db | 343 | smbfs_statfs(struct mount *mp, struct statfs *sbp, struct ucred *cred) |
984263bc MD |
344 | { |
345 | struct smbmount *smp = VFSTOSMBFS(mp); | |
346 | struct smbnode *np = smp->sm_root; | |
347 | struct smb_share *ssp = smp->sm_share; | |
348 | struct smb_cred scred; | |
349 | int error = 0; | |
350 | ||
351 | if (np == NULL) | |
352 | return EINVAL; | |
353 | ||
354 | sbp->f_iosize = SSTOVC(ssp)->vc_txmax; /* optimal transfer block size */ | |
355 | sbp->f_spare2 = 0; /* placeholder */ | |
acde96db | 356 | smb_makescred(&scred, curthread, cred); |
984263bc MD |
357 | |
358 | if (SMB_DIALECT(SSTOVC(ssp)) >= SMB_DIALECT_LANMAN2_0) | |
359 | error = smbfs_smb_statfs2(ssp, sbp, &scred); | |
360 | else | |
361 | error = smbfs_smb_statfs(ssp, sbp, &scred); | |
362 | if (error) | |
363 | return error; | |
364 | sbp->f_flags = 0; /* copy of mount exported flags */ | |
365 | if (sbp != &mp->mnt_stat) { | |
366 | sbp->f_fsid = mp->mnt_stat.f_fsid; /* file system id */ | |
367 | sbp->f_owner = mp->mnt_stat.f_owner; /* user that mounted the filesystem */ | |
368 | sbp->f_type = mp->mnt_vfc->vfc_typenum; /* type of filesystem */ | |
984263bc MD |
369 | bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN); |
370 | } | |
371 | strncpy(sbp->f_fstypename, mp->mnt_vfc->vfc_name, MFSNAMELEN); | |
372 | return 0; | |
373 | } | |
374 | ||
375 | /* | |
376 | * Flush out the buffer cache | |
377 | */ | |
378 | /* ARGSUSED */ | |
379 | static int | |
87de5057 | 380 | smbfs_sync(struct mount *mp, int waitfor) |
984263bc MD |
381 | { |
382 | struct vnode *vp; | |
383 | int error, allerror = 0; | |
384 | /* | |
385 | * Force stale buffer cache information to be flushed. | |
386 | */ | |
387 | loop: | |
388 | for (vp = TAILQ_FIRST(&mp->mnt_nvnodelist); | |
389 | vp != NULL; | |
390 | vp = TAILQ_NEXT(vp, v_nmntvnodes)) { | |
391 | /* | |
392 | * If the vnode that we are about to sync is no longer | |
393 | * associated with this mount point, start over. | |
394 | */ | |
395 | if (vp->v_mount != mp) | |
396 | goto loop; | |
a11aaa81 | 397 | if (vn_islocked(vp) || RB_EMPTY(&vp->v_rbdirty_tree) || |
984263bc MD |
398 | waitfor == MNT_LAZY) |
399 | continue; | |
87de5057 | 400 | if (vget(vp, LK_EXCLUSIVE)) |
984263bc | 401 | goto loop; |
87de5057 | 402 | error = VOP_FSYNC(vp, waitfor); |
984263bc MD |
403 | if (error) |
404 | allerror = error; | |
405 | vput(vp); | |
406 | } | |
407 | return (allerror); | |
408 | } | |
409 | ||
84c73a71 | 410 | #if defined(__FreeBSD__) && __FreeBSD_version < 400009 |
984263bc MD |
411 | /* |
412 | * smbfs flat namespace lookup. Unsupported. | |
413 | */ | |
414 | /* ARGSUSED */ | |
9c2a2cee | 415 | static int smbfs_vget(struct mount *mp, ino_t ino, struct vnode **vpp) |
984263bc MD |
416 | { |
417 | return (EOPNOTSUPP); | |
418 | } | |
419 | ||
420 | /* ARGSUSED */ | |
9c2a2cee CP |
421 | static int smbfs_fhtovp(struct mount *mp, struct fid *fhp, |
422 | struct sockaddr *nam, struct vnode **vpp, | |
423 | int *exflagsp, struct ucred **credanonp) | |
984263bc MD |
424 | { |
425 | return (EINVAL); | |
426 | } | |
427 | ||
428 | /* | |
429 | * Vnode pointer to File handle, should never happen either | |
430 | */ | |
431 | /* ARGSUSED */ | |
432 | static int | |
9c2a2cee | 433 | smbfs_vptofh(struct vnode *vp, struct fid *fhp) |
984263bc MD |
434 | { |
435 | return (EINVAL); | |
436 | } | |
437 | ||
438 | #endif /* __FreeBSD_version < 400009 */ |