1 /* $NetBSD: efs_vfsops.c,v 1.30 2022/03/19 13:53:32 hannken Exp $ */
2
3 /*
4 * Copyright (c) 2006 Stephen M. Rumble <rumble@ephemeral.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 #include <sys/cdefs.h>
20 __KERNEL_RCSID(0, "$NetBSD: efs_vfsops.c,v 1.30 2022/03/19 13:53:32 hannken Exp $");
21
22 #include <sys/param.h>
23 #include <sys/systm.h>
24 #include <sys/malloc.h>
25 #include <sys/mount.h>
26 #include <sys/fstypes.h>
27 #include <sys/vnode.h>
28 #include <sys/buf.h>
29 #include <sys/namei.h>
30 #include <sys/fcntl.h>
31 #include <sys/stat.h>
32 #include <sys/kauth.h>
33 #include <sys/proc.h>
34 #include <sys/module.h>
35
36 #include <miscfs/genfs/genfs_node.h>
37 #include <miscfs/genfs/genfs.h>
38
39 #include <miscfs/specfs/specdev.h>
40
41 #include <fs/efs/efs.h>
42 #include <fs/efs/efs_sb.h>
43 #include <fs/efs/efs_dir.h>
44 #include <fs/efs/efs_genfs.h>
45 #include <fs/efs/efs_mount.h>
46 #include <fs/efs/efs_extent.h>
47 #include <fs/efs/efs_dinode.h>
48 #include <fs/efs/efs_inode.h>
49 #include <fs/efs/efs_subr.h>
50
51 MODULE(MODULE_CLASS_VFS, efs, NULL);
52
53 MALLOC_JUSTDEFINE(M_EFSMNT, "efsmnt", "efs mount structure");
54 MALLOC_JUSTDEFINE(M_EFSINO, "efsino", "efs in-core inode structure");
55 MALLOC_JUSTDEFINE(M_EFSTMP, "efstmp", "efs temporary allocations");
56
57 extern int (**efs_vnodeop_p)(void *); /* for getnewvnode() */
58 extern int (**efs_specop_p)(void *); /* for getnewvnode() */
59 extern int (**efs_fifoop_p)(void *); /* for getnewvnode() */
60 static int efs_statvfs(struct mount *, struct statvfs *);
61
62 /*
63 * efs_mount and efs_mountroot common functions.
64 */
65 static int
efs_mount_common(struct mount * mp,const char * path,struct vnode * devvp,struct efs_args * args)66 efs_mount_common(struct mount *mp, const char *path, struct vnode *devvp,
67 struct efs_args *args)
68 {
69 int err;
70 struct buf *bp;
71 const char *why;
72 struct efs_mount *emp;
73 struct lwp *l = curlwp;
74
75 emp = malloc(sizeof(*emp), M_EFSMNT, M_WAITOK);
76 emp->em_dev = devvp->v_rdev;
77 emp->em_devvp = devvp;
78 emp->em_mnt = mp;
79
80 /* read in the superblock */
81 err = efs_bread(emp, EFS_BB_SB, l, &bp);
82 if (err) {
83 EFS_DPRINTF(("superblock read failed\n"));
84 free(emp, M_EFSMNT);
85 return (err);
86 }
87 memcpy(&emp->em_sb, bp->b_data, sizeof(emp->em_sb));
88 brelse(bp, 0);
89
90 /* validate the superblock */
91 if (efs_sb_validate(&emp->em_sb, &why)) {
92 printf("efs: invalid superblock: %s\n", why);
93 if (!(mp->mnt_flag & MNT_FORCE)) {
94 free(emp, M_EFSMNT);
95 return (EIO);
96 }
97 }
98
99 /* check that it's clean */
100 if (be16toh(emp->em_sb.sb_dirty) != EFS_SB_CLEAN) {
101 printf("efs: filesystem is dirty (sb_dirty = 0x%x); please "
102 "run fsck_efs(8)\n", be16toh(emp->em_sb.sb_dirty));
103 /* XXX - default to readonly unless forced?? */
104 }
105
106 /* if the superblock was replicated, verify that it is the same */
107 if (be32toh(emp->em_sb.sb_replsb) != 0) {
108 struct buf *rbp;
109 bool skip = false;
110
111 err = efs_bread(emp, be32toh(emp->em_sb.sb_replsb), l, &rbp);
112 if (err) {
113 printf("efs: read of superblock replicant failed; "
114 "please run fsck_efs(8)\n");
115 if (mp->mnt_flag & MNT_FORCE) {
116 skip = true;
117 } else {
118 free(emp, M_EFSMNT);
119 return (err);
120 }
121 }
122
123 if (!skip) {
124 if (memcmp(rbp->b_data, &emp->em_sb,
125 sizeof(emp->em_sb))) {
126 printf("efs: superblock differs from "
127 "replicant; please run fsck_efs(8)\n");
128 if (!(mp->mnt_flag & MNT_FORCE)) {
129 brelse(rbp, 0);
130 free(emp, M_EFSMNT);
131 return (EIO);
132 }
133 }
134 brelse(rbp, 0);
135 }
136 }
137
138 /* ensure we can read last block */
139 err = efs_bread(emp, be32toh(emp->em_sb.sb_size) - 1, l, &bp);
140 if (err) {
141 printf("efs: cannot access all filesystem blocks; please run "
142 "fsck_efs(8)\n");
143 if (!(mp->mnt_flag & MNT_FORCE)) {
144 free(emp, M_EFSMNT);
145 return (err);
146 }
147 } else {
148 brelse(bp, 0);
149 }
150
151 mp->mnt_data = emp;
152 mp->mnt_flag |= MNT_LOCAL;
153 mp->mnt_fs_bshift = EFS_BB_SHFT;
154 mp->mnt_dev_bshift = DEV_BSHIFT;
155 vfs_getnewfsid(mp);
156 efs_statvfs(mp, &mp->mnt_stat);
157
158 err = set_statvfs_info(path, UIO_USERSPACE, args->fspec,
159 UIO_USERSPACE, mp->mnt_op->vfs_name, mp, l);
160 if (err)
161 free(emp, M_EFSMNT);
162
163 return (err);
164 }
165
166 /*
167 * mount syscall vfsop.
168 *
169 * Returns 0 on success.
170 */
171 static int
efs_mount(struct mount * mp,const char * path,void * data,size_t * data_len)172 efs_mount(struct mount *mp, const char *path, void *data, size_t *data_len)
173 {
174 struct lwp *l = curlwp;
175 struct efs_args *args = data;
176 struct pathbuf *pb;
177 struct nameidata devnd;
178 struct efs_mount *emp;
179 struct vnode *devvp;
180 int err, mode;
181
182 if (args == NULL)
183 return EINVAL;
184 if (*data_len < sizeof *args)
185 return EINVAL;
186
187 if (mp->mnt_flag & MNT_GETARGS) {
188 if ((emp = VFSTOEFS(mp)) == NULL)
189 return (EIO);
190 args->fspec = NULL;
191 args->version = EFS_MNT_VERSION;
192 *data_len = sizeof *args;
193 return 0;
194 }
195
196 if (mp->mnt_flag & MNT_UPDATE)
197 return (EOPNOTSUPP); /* XXX read-only */
198
199 /* look up our device's vnode. it is returned locked */
200 err = pathbuf_copyin(args->fspec, &pb);
201 if (err) {
202 return err;
203 }
204 NDINIT(&devnd, LOOKUP, FOLLOW | LOCKLEAF, pb);
205 if ((err = namei(&devnd))) {
206 pathbuf_destroy(pb);
207 return (err);
208 }
209
210 devvp = devnd.ni_vp;
211 pathbuf_destroy(pb);
212
213 if (devvp->v_type != VBLK) {
214 vput(devvp);
215 return (ENOTBLK);
216 }
217
218 /* XXX - rdonly */
219 mode = FREAD;
220
221 /*
222 * If mount by non-root, then verify that user has necessary
223 * permissions on the device.
224 */
225 err = kauth_authorize_system(l->l_cred, KAUTH_SYSTEM_MOUNT,
226 KAUTH_REQ_SYSTEM_MOUNT_DEVICE, mp, devvp, KAUTH_ARG(VREAD));
227 if (err) {
228 vput(devvp);
229 return (err);
230 }
231
232 if ((err = VOP_OPEN(devvp, mode, l->l_cred))) {
233 vput(devvp);
234 return (err);
235 }
236
237 err = efs_mount_common(mp, path, devvp, args);
238 if (err) {
239 VOP_CLOSE(devvp, mode, l->l_cred);
240 vput(devvp);
241 return (err);
242 }
243
244 VOP_UNLOCK(devvp);
245
246 return (0);
247 }
248
249 /*
250 * Initialisation routine.
251 *
252 * Returns 0 on success.
253 */
254 static int
efs_start(struct mount * mp,int flags)255 efs_start(struct mount *mp, int flags)
256 {
257
258 return (0);
259 }
260
261 /*
262 * unmount syscall vfsop.
263 *
264 * Returns 0 on success.
265 */
266 static int
efs_unmount(struct mount * mp,int mntflags)267 efs_unmount(struct mount *mp, int mntflags)
268 {
269 struct efs_mount *emp;
270 struct lwp *l = curlwp;
271 int err;
272
273 emp = VFSTOEFS(mp);
274
275 err = vflush(mp, NULL, (mntflags & MNT_FORCE) ? FORCECLOSE : 0);
276 if (err)
277 return (err);
278
279 cache_purgevfs(mp);
280
281 vn_lock(emp->em_devvp, LK_EXCLUSIVE | LK_RETRY);
282 err = VOP_CLOSE(emp->em_devvp, FREAD, l->l_cred);
283 vput(emp->em_devvp);
284
285 free(mp->mnt_data, M_EFSMNT);
286 mp->mnt_data = NULL;
287 mp->mnt_flag &= ~MNT_LOCAL;
288
289 return (err);
290 }
291
292 /*
293 * Return the root vnode.
294 *
295 * Returns 0 on success.
296 */
297 static int
efs_root(struct mount * mp,int lktype,struct vnode ** vpp)298 efs_root(struct mount *mp, int lktype, struct vnode **vpp)
299 {
300 int err;
301 struct vnode *vp;
302
303 if ((err = VFS_VGET(mp, EFS_ROOTINO, lktype, &vp)))
304 return (err);
305
306 *vpp = vp;
307 return (0);
308 }
309
310 /*
311 * statvfs syscall vfsop.
312 *
313 * Returns 0 on success.
314 */
315 static int
efs_statvfs(struct mount * mp,struct statvfs * sbp)316 efs_statvfs(struct mount *mp, struct statvfs *sbp)
317 {
318 struct efs_mount *emp;
319
320 emp = VFSTOEFS(mp);
321 sbp->f_bsize = EFS_BB_SIZE;
322 sbp->f_frsize = EFS_BB_SIZE;
323 sbp->f_iosize = EFS_BB_SIZE;
324 sbp->f_blocks = be32toh(emp->em_sb.sb_size);
325 sbp->f_bfree = be32toh(emp->em_sb.sb_tfree);
326 sbp->f_bavail = sbp->f_bfree; // XXX same??
327 sbp->f_bresvd = 0;
328 sbp->f_files = be32toh(emp->em_sb.sb_tinode);
329 sbp->f_ffree = be16toh(emp->em_sb.sb_cgisize) *
330 be16toh(emp->em_sb.sb_ncg) *
331 EFS_DINODES_PER_BB;
332 sbp->f_favail = sbp->f_ffree; // XXX same??
333 sbp->f_fresvd = 0;
334 sbp->f_namemax = EFS_DIRENT_NAMELEN_MAX;
335 copy_statvfs_info(sbp, mp);
336
337 return (0);
338 }
339
340 /*
341 * Obtain a locked vnode for the given on-disk inode number.
342 *
343 * Returns 0 on success.
344 */
345 static int
efs_vget(struct mount * mp,ino_t ino,int lktype,struct vnode ** vpp)346 efs_vget(struct mount *mp, ino_t ino, int lktype, struct vnode **vpp)
347 {
348 int error;
349
350 error = vcache_get(mp, &ino, sizeof(ino), vpp);
351 if (error)
352 return error;
353 error = vn_lock(*vpp, lktype);
354 if (error) {
355 vrele(*vpp);
356 *vpp = NULL;
357 return error;
358 }
359 return 0;
360 }
361
362 /*
363 * Initialize this vnode / inode pair.
364 * Caller assures no other thread will try to load this inode.
365 */
366 static int
efs_loadvnode(struct mount * mp,struct vnode * vp,const void * key,size_t key_len,const void ** new_key)367 efs_loadvnode(struct mount *mp, struct vnode *vp,
368 const void *key, size_t key_len, const void **new_key)
369 {
370 int error;
371 ino_t ino;
372 struct efs_inode *eip;
373 struct efs_mount *emp;
374
375 KASSERT(key_len == sizeof(ino));
376 memcpy(&ino, key, key_len);
377 emp = VFSTOEFS(mp);
378
379 eip = pool_get(&efs_inode_pool, PR_WAITOK);
380 eip->ei_mode = 0;
381 eip->ei_lockf = NULL;
382 eip->ei_number = ino;
383 eip->ei_dev = emp->em_dev;
384 eip->ei_vp = vp;
385
386 error = efs_read_inode(emp, ino, NULL, &eip->ei_di);
387 if (error) {
388 pool_put(&efs_inode_pool, eip);
389 return error;
390 }
391
392 efs_sync_dinode_to_inode(eip);
393
394 if (ino == EFS_ROOTINO && !S_ISDIR(eip->ei_mode)) {
395 printf("efs: root inode (%lu) is not a directory!\n",
396 (ulong)EFS_ROOTINO);
397 pool_put(&efs_inode_pool, eip);
398 return EIO;
399 }
400
401 switch (eip->ei_mode & S_IFMT) {
402 case S_IFIFO:
403 vp->v_type = VFIFO;
404 vp->v_op = efs_fifoop_p;
405 break;
406 case S_IFCHR:
407 vp->v_type = VCHR;
408 vp->v_op = efs_specop_p;
409 spec_node_init(vp, eip->ei_dev);
410 break;
411 case S_IFDIR:
412 vp->v_type = VDIR;
413 vp->v_op = efs_vnodeop_p;
414 if (ino == EFS_ROOTINO)
415 vp->v_vflag |= VV_ROOT;
416 break;
417 case S_IFBLK:
418 vp->v_type = VBLK;
419 vp->v_op = efs_specop_p;
420 spec_node_init(vp, eip->ei_dev);
421 break;
422 case S_IFREG:
423 vp->v_type = VREG;
424 vp->v_op = efs_vnodeop_p;
425 break;
426 case S_IFLNK:
427 vp->v_type = VLNK;
428 vp->v_op = efs_vnodeop_p;
429 break;
430 case S_IFSOCK:
431 vp->v_type = VSOCK;
432 vp->v_op = efs_vnodeop_p;
433 break;
434 default:
435 printf("efs: invalid mode 0x%x in inode %lu on mount %s\n",
436 eip->ei_mode, (ulong)ino, mp->mnt_stat.f_mntonname);
437 pool_put(&efs_inode_pool, eip);
438 return EIO;
439 }
440
441 vp->v_tag = VT_EFS;
442 vp->v_data = eip;
443 genfs_node_init(vp, &efs_genfsops);
444 uvm_vnp_setsize(vp, eip->ei_size);
445 *new_key = &eip->ei_number;
446 return 0;
447 }
448
449 /*
450 * Convert the provided opaque, unique file handle into a vnode.
451 *
452 * Returns 0 on success.
453 */
454 static int
efs_fhtovp(struct mount * mp,struct fid * fhp,int lktype,struct vnode ** vpp)455 efs_fhtovp(struct mount *mp, struct fid *fhp, int lktype, struct vnode **vpp)
456 {
457 int err;
458 struct vnode *vp;
459 struct efs_fid *efp;
460 struct efs_inode *eip;
461
462 if (fhp->fid_len != sizeof(struct efs_fid))
463 return (EINVAL);
464
465 efp = (struct efs_fid *)fhp;
466
467 if ((err = VFS_VGET(mp, efp->ef_ino, lktype, &vp))) {
468 *vpp = NULL;
469 return (err);
470 }
471
472 eip = EFS_VTOI(vp);
473 if (eip->ei_mode == 0 || eip->ei_gen != efp->ef_gen) {
474 vput(vp);
475 *vpp = NULL;
476 return (ESTALE);
477 }
478
479 *vpp = vp;
480 return (0);
481 }
482
483 /*
484 * Convert the provided vnode into an opaque, unique file handle.
485 *
486 * Returns 0 on success.
487 */
488 static int
efs_vptofh(struct vnode * vp,struct fid * fhp,size_t * fh_size)489 efs_vptofh(struct vnode *vp, struct fid *fhp, size_t *fh_size)
490 {
491 struct efs_fid *efp;
492 struct efs_inode *eip;
493
494 if (*fh_size < sizeof(struct efs_fid)) {
495 *fh_size = sizeof(struct efs_fid);
496 return (E2BIG);
497 }
498 *fh_size = sizeof(struct efs_fid);
499
500 eip = EFS_VTOI(vp);
501 efp = (struct efs_fid *)fhp;
502
503 fhp->fid_len = sizeof(struct efs_fid);
504 efp->ef_ino = eip->ei_number;
505 efp->ef_gen = eip->ei_gen;
506
507 return (0);
508 }
509
510 /*
511 * Globally initialise the filesystem.
512 */
513 static void
efs_init(void)514 efs_init(void)
515 {
516
517 malloc_type_attach(M_EFSMNT);
518 malloc_type_attach(M_EFSINO);
519 malloc_type_attach(M_EFSTMP);
520 pool_init(&efs_inode_pool, sizeof(struct efs_inode), 0, 0, 0,
521 "efsinopl", &pool_allocator_nointr, IPL_NONE);
522 }
523
524 /*
525 * Globally reinitialise the filesystem.
526 */
527 static void
efs_reinit(void)528 efs_reinit(void)
529 {
530
531 }
532
533 /*
534 * Globally clean up the filesystem.
535 */
536 static void
efs_done(void)537 efs_done(void)
538 {
539
540 pool_destroy(&efs_inode_pool);
541 malloc_type_detach(M_EFSMNT);
542 malloc_type_detach(M_EFSINO);
543 malloc_type_detach(M_EFSTMP);
544 }
545
546 extern const struct vnodeopv_desc efs_vnodeop_opv_desc;
547 extern const struct vnodeopv_desc efs_specop_opv_desc;
548 extern const struct vnodeopv_desc efs_fifoop_opv_desc;
549
550 const struct vnodeopv_desc * const efs_vnodeopv_descs[] = {
551 &efs_vnodeop_opv_desc,
552 &efs_specop_opv_desc,
553 &efs_fifoop_opv_desc,
554 NULL
555 };
556
557 struct vfsops efs_vfsops = {
558 .vfs_name = MOUNT_EFS,
559 .vfs_min_mount_data = sizeof (struct efs_args),
560 .vfs_mount = efs_mount,
561 .vfs_start = efs_start,
562 .vfs_unmount = efs_unmount,
563 .vfs_root = efs_root,
564 .vfs_quotactl = (void *)eopnotsupp,
565 .vfs_statvfs = efs_statvfs,
566 .vfs_sync = (void *)nullop,
567 .vfs_vget = efs_vget,
568 .vfs_loadvnode = efs_loadvnode,
569 .vfs_fhtovp = efs_fhtovp,
570 .vfs_vptofh = efs_vptofh,
571 .vfs_init = efs_init,
572 .vfs_reinit = efs_reinit,
573 .vfs_done = efs_done,
574 .vfs_mountroot = (void *)eopnotsupp,
575 .vfs_snapshot = (void *)eopnotsupp,
576 .vfs_extattrctl = vfs_stdextattrctl,
577 .vfs_suspendctl = genfs_suspendctl,
578 .vfs_opv_descs = efs_vnodeopv_descs
579 /* .vfs_refcount */
580 /* .vfs_list */
581 };
582
583 static int
efs_modcmd(modcmd_t cmd,void * arg)584 efs_modcmd(modcmd_t cmd, void *arg)
585 {
586
587 switch (cmd) {
588 case MODULE_CMD_INIT:
589 return vfs_attach(&efs_vfsops);
590 case MODULE_CMD_FINI:
591 return vfs_detach(&efs_vfsops);
592 default:
593 return ENOTTY;
594 }
595 }
596