1/* $NetBSD: smbfs_vfsops.c,v 1.104 2014/12/21 10:48:53 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_vfsops.c,v 1.5 2001/12/13 13:08:34 sheldonh Exp
35 */
36
37#include <sys/cdefs.h>
38__KERNEL_RCSID(0, "$NetBSD: smbfs_vfsops.c,v 1.104 2014/12/21 10:48:53 hannken Exp $");
39
40#include <sys/param.h>
41#include <sys/systm.h>
42#include <sys/proc.h>
43#include <sys/buf.h>
44#include <sys/kernel.h>
45#include <sys/dirent.h>
46#include <sys/sysctl.h>
47#include <sys/vnode.h>
48#include <sys/mount.h>
49#include <sys/stat.h>
50#include <sys/malloc.h>
51#include <sys/kauth.h>
52#include <sys/module.h>
53#include <miscfs/genfs/genfs.h>
54
55
56#include <netsmb/smb.h>
57#include <netsmb/smb_conn.h>
58#include <netsmb/smb_subr.h>
59#include <netsmb/smb_dev.h>
60
61#include <fs/smbfs/smbfs.h>
62#include <fs/smbfs/smbfs_node.h>
63#include <fs/smbfs/smbfs_subr.h>
64
65MODULE(MODULE_CLASS_VFS, smbfs, "nsmb");
66
67VFS_PROTOS(smbfs);
68
69static struct sysctllog *smbfs_sysctl_log;
70
71static int smbfs_setroot(struct mount *);
72
73extern struct vnodeopv_desc smbfs_vnodeop_opv_desc;
74
75static const struct vnodeopv_desc *smbfs_vnodeopv_descs[] = {
76 &smbfs_vnodeop_opv_desc,
77 NULL,
78};
79
80struct vfsops smbfs_vfsops = {
81 .vfs_name = MOUNT_SMBFS,
82 .vfs_min_mount_data = sizeof (struct smbfs_args),
83 .vfs_mount = smbfs_mount,
84 .vfs_start = smbfs_start,
85 .vfs_unmount = smbfs_unmount,
86 .vfs_root = smbfs_root,
87 .vfs_quotactl = (void *)eopnotsupp,
88 .vfs_statvfs = smbfs_statvfs,
89 .vfs_sync = smbfs_sync,
90 .vfs_vget = smbfs_vget,
91 .vfs_loadvnode = smbfs_loadvnode,
92 .vfs_fhtovp = (void *)eopnotsupp,
93 .vfs_vptofh = (void *)eopnotsupp,
94 .vfs_init = smbfs_init,
95 .vfs_reinit = smbfs_reinit,
96 .vfs_done = smbfs_done,
97 .vfs_mountroot = (void *)eopnotsupp,
98 .vfs_snapshot = (void *)eopnotsupp,
99 .vfs_extattrctl = vfs_stdextattrctl,
100 .vfs_suspendctl = (void *)eopnotsupp,
101 .vfs_renamelock_enter = genfs_renamelock_enter,
102 .vfs_renamelock_exit = genfs_renamelock_exit,
103 .vfs_fsync = (void *)eopnotsupp,
104 .vfs_opv_descs = smbfs_vnodeopv_descs
105};
106
107static int
108smbfs_modcmd(modcmd_t cmd, void *arg)
109{
110 const struct sysctlnode *smb = NULL;
111 int error;
112
113 switch (cmd) {
114 case MODULE_CMD_INIT:
115 error = vfs_attach(&smbfs_vfsops);
116 if (error != 0)
117 break;
118 sysctl_createv(&smbfs_sysctl_log, 0, NULL, &smb,
119 CTLFLAG_PERMANENT,
120 CTLTYPE_NODE, "samba",
121 SYSCTL_DESCR("SMB/CIFS remote file system"),
122 NULL, 0, NULL, 0,
123 CTL_VFS, CTL_CREATE, CTL_EOL);
124
125 if (smb != NULL) {
126 sysctl_createv(&smbfs_sysctl_log, 0, &smb, NULL,
127 CTLFLAG_PERMANENT|CTLFLAG_IMMEDIATE,
128 CTLTYPE_INT, "version",
129 SYSCTL_DESCR("smbfs version"),
130 NULL, SMBFS_VERSION, NULL, 0,
131 CTL_CREATE, CTL_EOL);
132 }
133 break;
134 case MODULE_CMD_FINI:
135 error = vfs_detach(&smbfs_vfsops);
136 if (error != 0)
137 break;
138 sysctl_teardown(&smbfs_sysctl_log);
139 break;
140 default:
141 error = ENOTTY;
142 break;
143 }
144
145 return (error);
146}
147
148int
149smbfs_mount(struct mount *mp, const char *path, void *data, size_t *data_len)
150{
151 struct lwp *l = curlwp;
152 struct smbfs_args *args = data; /* holds data from mount request */
153 struct smbmount *smp = NULL;
154 struct smb_vc *vcp;
155 struct smb_share *ssp = NULL;
156 struct smb_cred scred;
157 char *fromname;
158 int error;
159
160 if (args == NULL)
161 return EINVAL;
162 if (*data_len < sizeof *args)
163 return EINVAL;
164
165 if (mp->mnt_flag & MNT_GETARGS) {
166 smp = VFSTOSMBFS(mp);
167 if (smp == NULL)
168 return EIO;
169 *args = smp->sm_args;
170 *data_len = sizeof *args;
171 return 0;
172 }
173
174 if (mp->mnt_flag & MNT_UPDATE)
175 return EOPNOTSUPP;
176
177 if (args->version != SMBFS_VERSION) {
178 SMBVDEBUG("mount version mismatch: kernel=%d, mount=%d\n",
179 SMBFS_VERSION, args->version);
180 return EINVAL;
181 }
182
183 smb_makescred(&scred, l, l->l_cred);
184 error = smb_dev2share(args->dev_fd, SMBM_EXEC, &scred, &ssp);
185 if (error)
186 return error;
187 smb_share_unlock(ssp); /* keep ref, but unlock */
188 vcp = SSTOVC(ssp);
189
190 fromname = kmem_zalloc(MNAMELEN, KM_SLEEP);
191 snprintf(fromname, MNAMELEN,
192 "//%s@%s/%s", vcp->vc_username, vcp->vc_srvname, ssp->ss_name);
193 error = set_statvfs_info(path, UIO_USERSPACE, fromname, UIO_SYSSPACE,
194 mp->mnt_op->vfs_name, mp, l);
195 kmem_free(fromname, MNAMELEN);
196 if (error) {
197 smb_share_lock(ssp);
198 smb_share_put(ssp, &scred);
199 return error;
200 }
201
202 mp->mnt_stat.f_iosize = vcp->vc_txmax;
203 mp->mnt_stat.f_namemax =
204 (vcp->vc_hflags2 & SMB_FLAGS2_KNOWS_LONG_NAMES) ? 255 : 12;
205
206 smp = malloc(sizeof(*smp), M_SMBFSDATA, M_WAITOK|M_ZERO);
207 mp->mnt_data = smp;
208
209 smp->sm_share = ssp;
210 smp->sm_root = NULL;
211 smp->sm_args = *args;
212 smp->sm_caseopt = args->caseopt;
213 smp->sm_args.file_mode = (smp->sm_args.file_mode &
214 (S_IRWXU|S_IRWXG|S_IRWXO)) | S_IFREG;
215 smp->sm_args.dir_mode = (smp->sm_args.dir_mode &
216 (S_IRWXU|S_IRWXG|S_IRWXO)) | S_IFDIR;
217
218 vfs_getnewfsid(mp);
219 return (0);
220}
221
222/* Unmount the filesystem described by mp. */
223int
224smbfs_unmount(struct mount *mp, int mntflags)
225{
226 struct lwp *l = curlwp;
227 struct smbmount *smp = VFSTOSMBFS(mp);
228 struct smb_cred scred;
229 struct vnode *smbfs_rootvp = SMBTOV(smp->sm_root);
230 int error, flags;
231
232 SMBVDEBUG("smbfs_unmount: flags=%04x\n", mntflags);
233 flags = 0;
234 if (mntflags & MNT_FORCE)
235 flags |= FORCECLOSE;
236
237 if (smbfs_rootvp->v_usecount > 1 && (mntflags & MNT_FORCE) == 0)
238 return EBUSY;
239
240 /* Flush all vnodes.
241 * Keep trying to flush the vnode list for the mount while
242 * some are still busy and we are making progress towards
243 * making them not busy. This is needed because smbfs vnodes
244 * reference their parent directory but may appear after their
245 * parent in the list; one pass over the vnode list is not
246 * sufficient in this case. */
247 do {
248 smp->sm_didrele = 0;
249 error = vflush(mp, smbfs_rootvp, flags);
250 } while (error == EBUSY && smp->sm_didrele != 0);
251 if (error)
252 return error;
253
254 vgone(smbfs_rootvp);
255
256 smb_makescred(&scred, l, l->l_cred);
257 smb_share_lock(smp->sm_share);
258 smb_share_put(smp->sm_share, &scred);
259 mp->mnt_data = NULL;
260
261 free(smp, M_SMBFSDATA);
262 return 0;
263}
264
265/*
266 * Get root vnode of the smbfs filesystem, and store it in sm_root.
267 */
268static int
269smbfs_setroot(struct mount *mp)
270{
271 struct smbmount *smp = VFSTOSMBFS(mp);
272 struct vnode *vp;
273 struct smbfattr fattr;
274 struct lwp *l = curlwp;
275 kauth_cred_t cred = l->l_cred;
276 struct smb_cred scred;
277 int error;
278
279 KASSERT(smp->sm_root == NULL);
280
281 smb_makescred(&scred, l, cred);
282 error = smbfs_smb_lookup(NULL, NULL, 0, &fattr, &scred);
283 if (error)
284 return error;
285 error = smbfs_nget(mp, NULL, "TheRooT", 7, &fattr, &vp);
286 if (error)
287 return error;
288
289 /*
290 * Someone might have already set sm_root while we slept
291 * in smb_lookup or vnode allocation.
292 */
293 if (smp->sm_root) {
294 KASSERT(smp->sm_root == VTOSMB(vp));
295 vrele(vp);
296 } else {
297 vp->v_vflag |= VV_ROOT;
298 smp->sm_root = VTOSMB(vp);
299 }
300
301 return (0);
302}
303
304/*
305 * Return locked root vnode of a filesystem.
306 */
307int
308smbfs_root(struct mount *mp, struct vnode **vpp)
309{
310 struct smbmount *smp = VFSTOSMBFS(mp);
311 int error;
312
313 if (__predict_false(!smp->sm_root)) {
314 error = smbfs_setroot(mp);
315 if (error)
316 return (error);
317 /* fallthrough */
318 }
319
320 KASSERT(smp->sm_root != NULL && SMBTOV(smp->sm_root) != NULL);
321 *vpp = SMBTOV(smp->sm_root);
322 vref(*vpp);
323 error = vn_lock(*vpp, LK_EXCLUSIVE | LK_RETRY);
324 if (error)
325 vrele(*vpp);
326 return error;
327}
328
329/*
330 * Make a filesystem operational.
331 * Nothing to do at the moment.
332 */
333/* ARGSUSED */
334int
335smbfs_start(struct mount *mp, int flags)
336{
337 SMBVDEBUG("flags=%04x\n", flags);
338 return 0;
339}
340
341void
342smbfs_init(void)
343{
344
345 malloc_type_attach(M_SMBNODENAME);
346 malloc_type_attach(M_SMBFSDATA);
347 pool_init(&smbfs_node_pool, sizeof(struct smbnode), 0, 0, 0,
348 "smbfsnopl", &pool_allocator_nointr, IPL_NONE);
349
350 SMBVDEBUG0("init.\n");
351}
352
353void
354smbfs_reinit(void)
355{
356
357 SMBVDEBUG0("reinit.\n");
358}
359
360void
361smbfs_done(void)
362{
363
364 pool_destroy(&smbfs_node_pool);
365 malloc_type_detach(M_SMBNODENAME);
366 malloc_type_detach(M_SMBFSDATA);
367
368 SMBVDEBUG0("done.\n");
369}
370
371/*
372 * smbfs_statvfs call
373 */
374int
375smbfs_statvfs(struct mount *mp, struct statvfs *sbp)
376{
377 struct lwp *l = curlwp;
378 struct smbmount *smp = VFSTOSMBFS(mp);
379 struct smb_share *ssp = smp->sm_share;
380 struct smb_cred scred;
381 int error = 0;
382
383 sbp->f_iosize = SSTOVC(ssp)->vc_txmax; /* optimal transfer block size */
384 smb_makescred(&scred, l, l->l_cred);
385
386 error = smbfs_smb_statvfs(ssp, sbp, &scred);
387 if (error)
388 return error;
389
390 sbp->f_flag = 0; /* copy of mount exported flags */
391 sbp->f_owner = mp->mnt_stat.f_owner; /* user that mounted the filesystem */
392 copy_statvfs_info(sbp, mp);
393 return 0;
394}
395
396static bool
397smbfs_sync_selector(void *cl, struct vnode *vp)
398{
399 struct smbnode *np;
400
401 np = VTOSMB(vp);
402 if (np == NULL)
403 return false;
404
405 if ((vp->v_type == VNON || (np->n_flag & NMODIFIED) == 0) &&
406 LIST_EMPTY(&vp->v_dirtyblkhd) &&
407 UVM_OBJ_IS_CLEAN(&vp->v_uobj))
408 return false;
409
410 return true;
411}
412
413/*
414 * Flush out the buffer cache
415 */
416int
417smbfs_sync(struct mount *mp, int waitfor, kauth_cred_t cred)
418{
419 struct vnode *vp;
420 struct vnode_iterator *marker;
421 int error, allerror = 0;
422
423 vfs_vnode_iterator_init(mp, &marker);
424 while ((vp = vfs_vnode_iterator_next(marker, smbfs_sync_selector,
425 NULL)))
426 {
427 error = vn_lock(vp, LK_EXCLUSIVE);
428 if (error) {
429 vrele(vp);
430 continue;
431 }
432 error = VOP_FSYNC(vp, cred,
433 waitfor == MNT_WAIT ? FSYNC_WAIT : 0, 0, 0);
434 if (error)
435 allerror = error;
436 vput(vp);
437 }
438 vfs_vnode_iterator_destroy(marker);
439 return (allerror);
440}
441
442#if __FreeBSD_version < 400009
443/*
444 * smbfs flat namespace lookup. Unsupported.
445 */
446/* ARGSUSED */
447int smbfs_vget(struct mount *mp, ino_t ino,
448 struct vnode **vpp)
449{
450 return (EOPNOTSUPP);
451}
452
453#endif /* __FreeBSD_version < 400009 */
454