1 /*-
2 * Copyright (c) 1994, 1995 The Regents of the University of California.
3 * Copyright (c) 1994, 1995 Jan-Simon Pendry.
4 * Copyright (c) 2005, 2006 Masanori Ozawa <ozawa@ongs.co.jp>, ONGS Inc.
5 * Copyright (c) 2006 Daichi Goto <daichi@freebsd.org>
6 * All rights reserved.
7 *
8 * This code is derived from software donated to Berkeley by
9 * Jan-Simon Pendry.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 4. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 *
35 * @(#)union_vfsops.c 8.20 (Berkeley) 5/20/95
36 * $FreeBSD$
37 */
38
39 #include <sys/param.h>
40 #include <sys/systm.h>
41 #include <sys/kdb.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/vnode.h>
49 #include <sys/stat.h>
50
51 #include <fs/unionfs/union.h>
52
53 static MALLOC_DEFINE(M_UNIONFSMNT, "UNIONFS mount", "UNIONFS mount structure");
54
55 static vfs_fhtovp_t unionfs_fhtovp;
56 static vfs_checkexp_t unionfs_checkexp;
57 static vfs_mount_t unionfs_domount;
58 static vfs_quotactl_t unionfs_quotactl;
59 static vfs_root_t unionfs_root;
60 static vfs_sync_t unionfs_sync;
61 static vfs_statfs_t unionfs_statfs;
62 static vfs_unmount_t unionfs_unmount;
63 static vfs_vget_t unionfs_vget;
64 static vfs_extattrctl_t unionfs_extattrctl;
65
66 static struct vfsops unionfs_vfsops;
67
68 /*
69 * Exchange from userland file mode to vmode.
70 */
71 static u_short
72 mode2vmode(mode_t mode)
73 {
74 u_short ret;
75
76 ret = 0;
77
78 /* other */
79 if (mode & S_IXOTH)
80 ret |= VEXEC >> 6;
81 if (mode & S_IWOTH)
82 ret |= VWRITE >> 6;
83 if (mode & S_IROTH)
84 ret |= VREAD >> 6;
85
86 /* group */
87 if (mode & S_IXGRP)
88 ret |= VEXEC >> 3;
89 if (mode & S_IWGRP)
90 ret |= VWRITE >> 3;
91 if (mode & S_IRGRP)
92 ret |= VREAD >> 3;
93
94 /* owner */
95 if (mode & S_IXUSR)
96 ret |= VEXEC;
97 if (mode & S_IWUSR)
98 ret |= VWRITE;
99 if (mode & S_IRUSR)
100 ret |= VREAD;
101
102 return (ret);
103 }
104
105 /*
106 * Mount unionfs layer.
107 */
108 static int
109 unionfs_domount(struct mount *mp, struct thread *td)
110 {
111 int error;
112 struct vnode *lowerrootvp;
113 struct vnode *upperrootvp;
114 struct unionfs_mount *ump;
115 char *target;
116 char *tmp;
117 char *ep;
118 int len;
119 size_t done;
120 int below;
121 uid_t uid;
122 gid_t gid;
123 u_short udir;
124 u_short ufile;
125 unionfs_copymode copymode;
126 unionfs_whitemode whitemode;
127 struct componentname fakecn;
128 struct nameidata nd, *ndp;
129 struct vattr va;
130
131 UNIONFSDEBUG("unionfs_mount(mp = %p)\n", (void *)mp);
132
133 error = 0;
134 below = 0;
135 uid = 0;
136 gid = 0;
137 udir = 0;
138 ufile = 0;
139 copymode = UNIONFS_TRANSPARENT; /* default */
140 whitemode = UNIONFS_WHITE_ALWAYS;
141 ndp = &nd;
142
143 if (mp->mnt_flag & MNT_ROOTFS) {
144 vfs_mount_error(mp, "Cannot union mount root filesystem");
145 return (EOPNOTSUPP);
146 }
147
148 /*
149 * Update is a no operation.
150 */
151 if (mp->mnt_flag & MNT_UPDATE) {
152 vfs_mount_error(mp, "unionfs does not support mount update");
153 return (EOPNOTSUPP);
154 }
155
156 /*
157 * Get argument
158 */
159 error = vfs_getopt(mp->mnt_optnew, "target", (void **)&target, &len);
160 if (error)
161 error = vfs_getopt(mp->mnt_optnew, "from", (void **)&target,
162 &len);
163 if (error || target[len - 1] != '\0') {
164 vfs_mount_error(mp, "Invalid target");
165 return (EINVAL);
166 }
167 if (vfs_getopt(mp->mnt_optnew, "below", NULL, NULL) == 0)
168 below = 1;
169 if (vfs_getopt(mp->mnt_optnew, "udir", (void **)&tmp, NULL) == 0) {
170 if (tmp != NULL)
171 udir = (mode_t)strtol(tmp, &ep, 8);
172 if (tmp == NULL || *ep) {
173 vfs_mount_error(mp, "Invalid udir");
174 return (EINVAL);
175 }
176 udir = mode2vmode(udir);
177 }
178 if (vfs_getopt(mp->mnt_optnew, "ufile", (void **)&tmp, NULL) == 0) {
179 if (tmp != NULL)
180 ufile = (mode_t)strtol(tmp, &ep, 8);
181 if (tmp == NULL || *ep) {
182 vfs_mount_error(mp, "Invalid ufile");
183 return (EINVAL);
184 }
185 ufile = mode2vmode(ufile);
186 }
187 /* check umask, uid and gid */
188 if (udir == 0 && ufile != 0)
189 udir = ufile;
190 if (ufile == 0 && udir != 0)
191 ufile = udir;
192
193 vn_lock(mp->mnt_vnodecovered, LK_SHARED | LK_RETRY, td);
194 error = VOP_GETATTR(mp->mnt_vnodecovered, &va, mp->mnt_cred, td);
195 if (!error) {
196 if (udir == 0)
197 udir = va.va_mode;
198 if (ufile == 0)
199 ufile = va.va_mode;
200 uid = va.va_uid;
201 gid = va.va_gid;
202 }
203 VOP_UNLOCK(mp->mnt_vnodecovered, 0, td);
204 if (error)
205 return (error);
206
207 if (mp->mnt_cred->cr_ruid == 0) { /* root only */
208 if (vfs_getopt(mp->mnt_optnew, "uid", (void **)&tmp,
209 NULL) == 0) {
210 if (tmp != NULL)
211 uid = (uid_t)strtol(tmp, &ep, 10);
212 if (tmp == NULL || *ep) {
213 vfs_mount_error(mp, "Invalid uid");
214 return (EINVAL);
215 }
216 }
217 if (vfs_getopt(mp->mnt_optnew, "gid", (void **)&tmp,
218 NULL) == 0) {
219 if (tmp != NULL)
220 gid = (gid_t)strtol(tmp, &ep, 10);
221 if (tmp == NULL || *ep) {
222 vfs_mount_error(mp, "Invalid gid");
223 return (EINVAL);
224 }
225 }
226 if (vfs_getopt(mp->mnt_optnew, "copymode", (void **)&tmp,
227 NULL) == 0) {
228 if (tmp == NULL) {
229 vfs_mount_error(mp, "Invalid copymode");
230 return (EINVAL);
231 } else if (strcasecmp(tmp, "traditional") == 0)
232 copymode = UNIONFS_TRADITIONAL;
233 else if (strcasecmp(tmp, "transparent") == 0)
234 copymode = UNIONFS_TRANSPARENT;
235 else if (strcasecmp(tmp, "masquerade") == 0)
236 copymode = UNIONFS_MASQUERADE;
237 else {
238 vfs_mount_error(mp, "Invalid copymode");
239 return (EINVAL);
240 }
241 }
242 if (vfs_getopt(mp->mnt_optnew, "whiteout", (void **)&tmp,
243 NULL) == 0) {
244 if (tmp == NULL) {
245 vfs_mount_error(mp, "Invalid whiteout mode");
246 return (EINVAL);
247 } else if (strcasecmp(tmp, "always") == 0)
248 whitemode = UNIONFS_WHITE_ALWAYS;
249 else if (strcasecmp(tmp, "whenneeded") == 0)
250 whitemode = UNIONFS_WHITE_WHENNEEDED;
251 else {
252 vfs_mount_error(mp, "Invalid whiteout mode");
253 return (EINVAL);
254 }
255 }
256 }
257 /* If copymode is UNIONFS_TRADITIONAL, uid/gid is mounted user. */
258 if (copymode == UNIONFS_TRADITIONAL) {
259 uid = mp->mnt_cred->cr_ruid;
260 gid = mp->mnt_cred->cr_rgid;
261 }
262
263 UNIONFSDEBUG("unionfs_mount: uid=%d, gid=%d\n", uid, gid);
264 UNIONFSDEBUG("unionfs_mount: udir=0%03o, ufile=0%03o\n", udir, ufile);
265 UNIONFSDEBUG("unionfs_mount: copymode=%d\n", copymode);
266
267 /*
268 * Find upper node
269 */
270 NDINIT(ndp, LOOKUP, FOLLOW | WANTPARENT | LOCKLEAF, UIO_SYSSPACE, target, td);
271 if ((error = namei(ndp)))
272 return (error);
273
274 NDFREE(ndp, NDF_ONLY_PNBUF);
275
276 /* get root vnodes */
277 lowerrootvp = mp->mnt_vnodecovered;
278 upperrootvp = ndp->ni_vp;
279
280 vrele(ndp->ni_dvp);
281 ndp->ni_dvp = NULLVP;
282
283 /* create unionfs_mount */
284 ump = (struct unionfs_mount *)malloc(sizeof(struct unionfs_mount),
285 M_UNIONFSMNT, M_WAITOK | M_ZERO);
286
287 /*
288 * Save reference
289 */
290 if (below) {
291 VOP_UNLOCK(upperrootvp, 0, td);
292 vn_lock(lowerrootvp, LK_EXCLUSIVE | LK_RETRY, td);
293 ump->um_lowervp = upperrootvp;
294 ump->um_uppervp = lowerrootvp;
295 } else {
296 ump->um_lowervp = lowerrootvp;
297 ump->um_uppervp = upperrootvp;
298 }
299 ump->um_rootvp = NULLVP;
300 ump->um_uid = uid;
301 ump->um_gid = gid;
302 ump->um_udir = udir;
303 ump->um_ufile = ufile;
304 ump->um_copymode = copymode;
305 ump->um_whitemode = whitemode;
306
307 MNT_ILOCK(mp);
308 if ((lowerrootvp->v_mount->mnt_kern_flag & MNTK_MPSAFE) &&
309 (upperrootvp->v_mount->mnt_kern_flag & MNTK_MPSAFE))
310 mp->mnt_kern_flag |= MNTK_MPSAFE;
311 MNT_IUNLOCK(mp);
312 mp->mnt_data = (qaddr_t)ump;
313
314 /*
315 * Copy upper layer's RDONLY flag.
316 */
317 mp->mnt_flag |= ump->um_uppervp->v_mount->mnt_flag & MNT_RDONLY;
318
319 /*
320 * Check whiteout
321 */
322 if ((mp->mnt_flag & MNT_RDONLY) == 0) {
323 memset(&fakecn, 0, sizeof(fakecn));
324 fakecn.cn_nameiop = LOOKUP;
325 fakecn.cn_thread = td;
326 error = VOP_WHITEOUT(ump->um_uppervp, &fakecn, LOOKUP);
327 if (error) {
328 if (below) {
329 VOP_UNLOCK(ump->um_uppervp, 0, td);
330 vrele(upperrootvp);
331 } else
332 vput(ump->um_uppervp);
333 free(ump, M_UNIONFSMNT);
334 mp->mnt_data = NULL;
335 return (error);
336 }
337 }
338
339 /*
340 * Unlock the node
341 */
342 VOP_UNLOCK(ump->um_uppervp, 0, td);
343
344 /*
345 * Get the unionfs root vnode.
346 */
347 error = unionfs_nodeget(mp, ump->um_uppervp, ump->um_lowervp,
348 NULLVP, &(ump->um_rootvp), NULL, td);
349 vrele(upperrootvp);
350 if (error) {
351 free(ump, M_UNIONFSMNT);
352 mp->mnt_data = NULL;
353 return (error);
354 }
355
356 /*
357 * Check mnt_flag
358 */
359 if ((ump->um_lowervp->v_mount->mnt_flag & MNT_LOCAL) &&
360 (ump->um_uppervp->v_mount->mnt_flag & MNT_LOCAL))
361 mp->mnt_flag |= MNT_LOCAL;
362
363 /*
364 * Get new fsid
365 */
366 vfs_getnewfsid(mp);
367
368 len = MNAMELEN - 1;
369 tmp = mp->mnt_stat.f_mntfromname;
370 copystr((below ? "<below>:" : "<above>:"), tmp, len, &done);
371 len -= done - 1;
372 tmp += done - 1;
373 copystr(target, tmp, len, NULL);
374
375 UNIONFSDEBUG("unionfs_mount: from %s, on %s\n",
376 mp->mnt_stat.f_mntfromname, mp->mnt_stat.f_mntonname);
377
378 return (0);
379 }
380
381 /*
382 * Free reference to unionfs layer
383 */
384 static int
385 unionfs_unmount(struct mount *mp, int mntflags, struct thread *td)
386 {
387 struct unionfs_mount *ump;
388 int error;
389 int num;
390 int freeing;
391 int flags;
392
393 UNIONFSDEBUG("unionfs_unmount: mp = %p\n", (void *)mp);
394
395 ump = MOUNTTOUNIONFSMOUNT(mp);
396 flags = 0;
397
398 if (mntflags & MNT_FORCE)
399 flags |= FORCECLOSE;
400
401 /* vflush (no need to call vrele) */
402 for (freeing = 0; (error = vflush(mp, 1, flags, td)) != 0;) {
403 num = mp->mnt_nvnodelistsize;
404 if (num == freeing)
405 break;
406 freeing = num;
407 }
408
409 if (error)
410 return (error);
411
412 free(ump, M_UNIONFSMNT);
413 mp->mnt_data = 0;
414
415 return (0);
416 }
417
418 static int
419 unionfs_root(struct mount *mp, int flags, struct vnode **vpp, struct thread *td)
420 {
421 struct unionfs_mount *ump;
422 struct vnode *vp;
423
424 ump = MOUNTTOUNIONFSMOUNT(mp);
425 vp = ump->um_rootvp;
426
427 UNIONFSDEBUG("unionfs_root: rootvp=%p locked=%x\n",
428 vp, VOP_ISLOCKED(vp, td));
429
430 vref(vp);
431 if (flags & LK_TYPE_MASK)
432 vn_lock(vp, flags, td);
433
434 *vpp = vp;
435
436 return (0);
437 }
438
439 static int
440 unionfs_quotactl(struct mount *mp, int cmd, uid_t uid, void *arg,
441 struct thread *td)
442 {
443 struct unionfs_mount *ump;
444
445 ump = MOUNTTOUNIONFSMOUNT(mp);
446
447 /*
448 * Writing is always performed to upper vnode.
449 */
450 return (VFS_QUOTACTL(ump->um_uppervp->v_mount, cmd, uid, arg, td));
451 }
452
453 static int
454 unionfs_statfs(struct mount *mp, struct statfs *sbp, struct thread *td)
455 {
456 struct unionfs_mount *ump;
457 int error;
458 struct statfs mstat;
459 uint64_t lbsize;
460
461 ump = MOUNTTOUNIONFSMOUNT(mp);
462
463 UNIONFSDEBUG("unionfs_statfs(mp = %p, lvp = %p, uvp = %p)\n",
464 (void *)mp, (void *)ump->um_lowervp, (void *)ump->um_uppervp);
465
466 bzero(&mstat, sizeof(mstat));
467
468 error = VFS_STATFS(ump->um_lowervp->v_mount, &mstat, td);
469 if (error)
470 return (error);
471
472 /* now copy across the "interesting" information and fake the rest */
473 sbp->f_blocks = mstat.f_blocks;
474 sbp->f_files = mstat.f_files;
475
476 lbsize = mstat.f_bsize;
477
478 error = VFS_STATFS(ump->um_uppervp->v_mount, &mstat, td);
479 if (error)
480 return (error);
481
482 /*
483 * The FS type etc is copy from upper vfs.
484 * (write able vfs have priority)
485 */
486 sbp->f_type = mstat.f_type;
487 sbp->f_flags = mstat.f_flags;
488 sbp->f_bsize = mstat.f_bsize;
489 sbp->f_iosize = mstat.f_iosize;
490
491 if (mstat.f_bsize != lbsize)
492 sbp->f_blocks = ((off_t)sbp->f_blocks * lbsize) / mstat.f_bsize;
493
494 sbp->f_blocks += mstat.f_blocks;
495 sbp->f_bfree = mstat.f_bfree;
496 sbp->f_bavail = mstat.f_bavail;
497 sbp->f_files += mstat.f_files;
498 sbp->f_ffree = mstat.f_ffree;
499 return (0);
500 }
501
502 static int
503 unionfs_sync(struct mount *mp, int waitfor, struct thread *td)
504 {
505 /* nothing to do */
506 return (0);
507 }
508
509 static int
510 unionfs_vget(struct mount *mp, ino_t ino, int flags, struct vnode **vpp)
511 {
512 return (EOPNOTSUPP);
513 }
514
515 static int
516 unionfs_fhtovp(struct mount *mp, struct fid *fidp, struct vnode **vpp)
517 {
518 return (EOPNOTSUPP);
519 }
520
521 static int
522 unionfs_checkexp(struct mount *mp, struct sockaddr *nam, int *extflagsp,
523 struct ucred **credanonp)
524 {
525 return (EOPNOTSUPP);
526 }
527
528 static int
529 unionfs_extattrctl(struct mount *mp, int cmd, struct vnode *filename_vp,
530 int namespace, const char *attrname, struct thread *td)
531 {
532 struct unionfs_mount *ump;
533 struct unionfs_node *unp;
534
535 ump = MOUNTTOUNIONFSMOUNT(mp);
536 unp = VTOUNIONFS(filename_vp);
537
538 if (unp->un_uppervp != NULLVP) {
539 return (VFS_EXTATTRCTL(ump->um_uppervp->v_mount, cmd,
540 unp->un_uppervp, namespace, attrname, td));
541 } else {
542 return (VFS_EXTATTRCTL(ump->um_lowervp->v_mount, cmd,
543 unp->un_lowervp, namespace, attrname, td));
544 }
545 }
546
547 static struct vfsops unionfs_vfsops = {
548 .vfs_checkexp = unionfs_checkexp,
549 .vfs_extattrctl = unionfs_extattrctl,
550 .vfs_fhtovp = unionfs_fhtovp,
551 .vfs_init = unionfs_init,
552 .vfs_mount = unionfs_domount,
553 .vfs_quotactl = unionfs_quotactl,
554 .vfs_root = unionfs_root,
555 .vfs_statfs = unionfs_statfs,
556 .vfs_sync = unionfs_sync,
557 .vfs_uninit = unionfs_uninit,
558 .vfs_unmount = unionfs_unmount,
559 .vfs_vget = unionfs_vget,
560 };
561
562 VFS_SET(unionfs_vfsops, unionfs, VFCF_LOOPBACK);
Cache object: 9d4b57c1542d7ed004d8d74ca81ffee0
|