1 /* $OpenBSD: fuse_vfsops.c,v 1.44 2020/11/17 03:23:10 gnezdo Exp $ */ 2 /* 3 * Copyright (c) 2012-2013 Sylvestre Gallon <ccna.syl@gmail.com> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include <sys/param.h> 19 #include <sys/systm.h> 20 #include <sys/file.h> 21 #include <sys/filedesc.h> 22 #include <sys/malloc.h> 23 #include <sys/mount.h> 24 #include <sys/pool.h> 25 #include <sys/proc.h> 26 #include <sys/specdev.h> 27 #include <sys/stat.h> 28 #include <sys/statvfs.h> 29 #include <sys/sysctl.h> 30 #include <sys/vnode.h> 31 #include <sys/fusebuf.h> 32 33 #include "fusefs_node.h" 34 #include "fusefs.h" 35 36 int fusefs_mount(struct mount *, const char *, void *, struct nameidata *, 37 struct proc *); 38 int fusefs_start(struct mount *, int, struct proc *); 39 int fusefs_unmount(struct mount *, int, struct proc *); 40 int fusefs_root(struct mount *, struct vnode **); 41 int fusefs_quotactl(struct mount *, int, uid_t, caddr_t, struct proc *); 42 int fusefs_statfs(struct mount *, struct statfs *, struct proc *); 43 int fusefs_sync(struct mount *, int, int, struct ucred *, struct proc *); 44 int fusefs_vget(struct mount *, ino_t, struct vnode **); 45 int fusefs_fhtovp(struct mount *, struct fid *, struct vnode **); 46 int fusefs_vptofh(struct vnode *, struct fid *); 47 int fusefs_init(struct vfsconf *); 48 int fusefs_sysctl(int *, u_int, void *, size_t *, void *, size_t, 49 struct proc *); 50 int fusefs_checkexp(struct mount *, struct mbuf *, int *, 51 struct ucred **); 52 53 const struct vfsops fusefs_vfsops = { 54 .vfs_mount = fusefs_mount, 55 .vfs_start = fusefs_start, 56 .vfs_unmount = fusefs_unmount, 57 .vfs_root = fusefs_root, 58 .vfs_quotactl = fusefs_quotactl, 59 .vfs_statfs = fusefs_statfs, 60 .vfs_sync = fusefs_sync, 61 .vfs_vget = fusefs_vget, 62 .vfs_fhtovp = fusefs_fhtovp, 63 .vfs_vptofh = fusefs_vptofh, 64 .vfs_init = fusefs_init, 65 .vfs_sysctl = fusefs_sysctl, 66 .vfs_checkexp = fusefs_checkexp, 67 }; 68 69 struct pool fusefs_fbuf_pool; 70 71 #define PENDING 2 /* FBT_INIT reply not yet received */ 72 73 int 74 fusefs_mount(struct mount *mp, const char *path, void *data, 75 struct nameidata *ndp, struct proc *p) 76 { 77 struct fusefs_mnt *fmp; 78 struct fusebuf *fbuf; 79 struct fusefs_args *args = data; 80 struct vnode *vp; 81 struct file *fp; 82 int error = 0; 83 84 if (mp->mnt_flag & MNT_UPDATE) 85 return (EOPNOTSUPP); 86 87 if ((fp = fd_getfile(p->p_fd, args->fd)) == NULL) 88 return (EBADF); 89 90 if (fp->f_type != DTYPE_VNODE) { 91 error = EINVAL; 92 goto bad; 93 } 94 95 vp = fp->f_data; 96 if (vp->v_type != VCHR) { 97 error = EBADF; 98 goto bad; 99 } 100 101 /* Only root may specify allow_other. */ 102 if (args->allow_other && (error = suser_ucred(p->p_ucred))) 103 goto bad; 104 105 fmp = malloc(sizeof(*fmp), M_FUSEFS, M_WAITOK | M_ZERO); 106 fmp->mp = mp; 107 fmp->sess_init = PENDING; 108 fmp->dev = vp->v_rdev; 109 if (args->max_read > 0) 110 fmp->max_read = MIN(args->max_read, FUSEBUFMAXSIZE); 111 else 112 fmp->max_read = FUSEBUFMAXSIZE; 113 114 fmp->allow_other = args->allow_other; 115 116 mp->mnt_data = fmp; 117 mp->mnt_flag |= MNT_LOCAL; 118 vfs_getnewfsid(mp); 119 120 memset(mp->mnt_stat.f_mntonname, 0, MNAMELEN); 121 strlcpy(mp->mnt_stat.f_mntonname, path, MNAMELEN); 122 memset(mp->mnt_stat.f_mntfromname, 0, MNAMELEN); 123 strlcpy(mp->mnt_stat.f_mntfromname, "fusefs", MNAMELEN); 124 memset(mp->mnt_stat.f_mntfromspec, 0, MNAMELEN); 125 strlcpy(mp->mnt_stat.f_mntfromspec, "fusefs", MNAMELEN); 126 127 fuse_device_set_fmp(fmp, 1); 128 fbuf = fb_setup(0, 0, FBT_INIT, p); 129 130 /* cannot tsleep on mount */ 131 fuse_device_queue_fbuf(fmp->dev, fbuf); 132 133 bad: 134 FRELE(fp, p); 135 return (error); 136 } 137 138 int 139 fusefs_start(struct mount *mp, int flags, struct proc *p) 140 { 141 return (0); 142 } 143 144 int 145 fusefs_unmount(struct mount *mp, int mntflags, struct proc *p) 146 { 147 struct fusefs_mnt *fmp; 148 struct fusebuf *fbuf; 149 int flags = 0; 150 int error; 151 152 fmp = VFSTOFUSEFS(mp); 153 154 if (mntflags & MNT_FORCE) 155 flags |= FORCECLOSE; 156 157 if ((error = vflush(mp, NULLVP, flags))) 158 return (error); 159 160 if (fmp->sess_init && fmp->sess_init != PENDING) { 161 fbuf = fb_setup(0, 0, FBT_DESTROY, p); 162 163 error = fb_queue(fmp->dev, fbuf); 164 165 if (error) 166 printf("fusefs: error %d on destroy\n", error); 167 168 fb_delete(fbuf); 169 } 170 fmp->sess_init = 0; 171 172 fuse_device_cleanup(fmp->dev); 173 fuse_device_set_fmp(fmp, 0); 174 free(fmp, M_FUSEFS, sizeof(*fmp)); 175 mp->mnt_data = NULL; 176 177 return (0); 178 } 179 180 int 181 fusefs_root(struct mount *mp, struct vnode **vpp) 182 { 183 struct vnode *nvp; 184 int error; 185 186 if ((error = VFS_VGET(mp, FUSE_ROOTINO, &nvp)) != 0) 187 return (error); 188 189 nvp->v_type = VDIR; 190 191 *vpp = nvp; 192 return (0); 193 } 194 195 int 196 fusefs_quotactl(struct mount *mp, int cmds, uid_t uid, caddr_t arg, 197 struct proc *p) 198 { 199 return (EOPNOTSUPP); 200 } 201 202 int 203 fusefs_statfs(struct mount *mp, struct statfs *sbp, struct proc *p) 204 { 205 struct fusefs_mnt *fmp; 206 struct fusebuf *fbuf; 207 int error; 208 209 fmp = VFSTOFUSEFS(mp); 210 211 /* Deny other users unless allow_other mount option was specified. */ 212 if (!fmp->allow_other && p->p_ucred->cr_uid != mp->mnt_stat.f_owner) 213 return (EPERM); 214 215 copy_statfs_info(sbp, mp); 216 217 /* 218 * Both FBT_INIT and FBT_STATFS are sent to the FUSE file system 219 * daemon when it is mounted. However, the daemon is the process 220 * that called mount(2) so to prevent a deadlock return dummy 221 * values until the response to FBT_INIT init is received. All 222 * other VFS syscalls are queued. 223 */ 224 if (!fmp->sess_init || fmp->sess_init == PENDING) { 225 sbp->f_bavail = 0; 226 sbp->f_bfree = 0; 227 sbp->f_blocks = 0; 228 sbp->f_ffree = 0; 229 sbp->f_favail = 0; 230 sbp->f_files = 0; 231 sbp->f_bsize = 0; 232 sbp->f_iosize = 0; 233 sbp->f_namemax = 0; 234 } else { 235 fbuf = fb_setup(0, FUSE_ROOTINO, FBT_STATFS, p); 236 237 error = fb_queue(fmp->dev, fbuf); 238 239 if (error) { 240 fb_delete(fbuf); 241 return (error); 242 } 243 244 sbp->f_bavail = fbuf->fb_stat.f_bavail; 245 sbp->f_bfree = fbuf->fb_stat.f_bfree; 246 sbp->f_blocks = fbuf->fb_stat.f_blocks; 247 sbp->f_files = fbuf->fb_stat.f_files; 248 sbp->f_ffree = fbuf->fb_stat.f_ffree; 249 sbp->f_favail = fbuf->fb_stat.f_favail; 250 sbp->f_bsize = fbuf->fb_stat.f_frsize; 251 sbp->f_iosize = fbuf->fb_stat.f_bsize; 252 sbp->f_namemax = fbuf->fb_stat.f_namemax; 253 fb_delete(fbuf); 254 } 255 256 return (0); 257 } 258 259 int 260 fusefs_sync(struct mount *mp, int waitfor, int stall, struct ucred *cred, 261 struct proc *p) 262 { 263 return (0); 264 } 265 266 int 267 fusefs_vget(struct mount *mp, ino_t ino, struct vnode **vpp) 268 { 269 struct vattr vattr; 270 struct fusefs_mnt *fmp; 271 struct fusefs_node *ip; 272 struct vnode *nvp; 273 int i; 274 int error; 275 retry: 276 fmp = VFSTOFUSEFS(mp); 277 /* 278 * check if vnode is in hash. 279 */ 280 if ((*vpp = ufs_ihashget(fmp->dev, ino)) != NULLVP) 281 return (0); 282 283 /* 284 * if not create it 285 */ 286 if ((error = getnewvnode(VT_FUSEFS, mp, &fusefs_vops, &nvp)) != 0) { 287 printf("fusefs: getnewvnode error\n"); 288 *vpp = NULLVP; 289 return (error); 290 } 291 292 ip = malloc(sizeof(*ip), M_FUSEFS, M_WAITOK | M_ZERO); 293 rrw_init_flags(&ip->ufs_ino.i_lock, "fuseinode", 294 RWL_DUPOK | RWL_IS_VNODE); 295 nvp->v_data = ip; 296 ip->ufs_ino.i_vnode = nvp; 297 ip->ufs_ino.i_dev = fmp->dev; 298 ip->ufs_ino.i_number = ino; 299 300 for (i = 0; i < FUFH_MAXTYPE; i++) 301 ip->fufh[i].fh_type = FUFH_INVALID; 302 303 error = ufs_ihashins(&ip->ufs_ino); 304 if (error) { 305 vrele(nvp); 306 307 if (error == EEXIST) 308 goto retry; 309 310 return (error); 311 } 312 313 ip->ufs_ino.i_ump = (struct ufsmount *)fmp; 314 315 if (ino == FUSE_ROOTINO) 316 nvp->v_flag |= VROOT; 317 else { 318 /* 319 * Initialise the file size so that file size changes can be 320 * detected during file operations. 321 */ 322 error = VOP_GETATTR(nvp, &vattr, curproc->p_ucred, curproc); 323 if (error) { 324 vrele(nvp); 325 return (error); 326 } 327 ip->filesize = vattr.va_size; 328 } 329 330 *vpp = nvp; 331 332 return (0); 333 } 334 335 int 336 fusefs_fhtovp(struct mount *mp, struct fid *fhp, struct vnode **vpp) 337 { 338 return (EINVAL); 339 } 340 341 int 342 fusefs_vptofh(struct vnode *vp, struct fid *fhp) 343 { 344 return (EINVAL); 345 } 346 347 int 348 fusefs_init(struct vfsconf *vfc) 349 { 350 pool_init(&fusefs_fbuf_pool, sizeof(struct fusebuf), 0, 0, PR_WAITOK, 351 "fmsg", NULL); 352 353 return (0); 354 } 355 356 extern int stat_fbufs_in, stat_fbufs_wait, stat_opened_fusedev; 357 358 const struct sysctl_bounded_args fusefs_vars[] = { 359 { FUSEFS_OPENDEVS, &stat_opened_fusedev, 1, 0 }, 360 { FUSEFS_INFBUFS, &stat_fbufs_in, 1, 0 }, 361 { FUSEFS_WAITFBUFS, &stat_fbufs_wait, 1, 0 }, 362 { FUSEFS_POOL_NBPAGES, &fusefs_fbuf_pool.pr_npages, 1, 0 }, 363 }; 364 365 int 366 fusefs_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, 367 void *newp, size_t newlen, struct proc *p) 368 { 369 return sysctl_bounded_arr(fusefs_vars, nitems(fusefs_vars), name, 370 namelen, oldp, oldlenp, newp, newlen); 371 } 372 373 int 374 fusefs_checkexp(struct mount *mp, struct mbuf *nam, int *extflagsp, 375 struct ucred **credanonp) 376 { 377 return (EOPNOTSUPP); 378 } 379