1 /* 2 * Copyright (c) 2013 The DragonFly Project. All rights reserved. 3 * 4 * This code is derived from software contributed to The DragonFly Project 5 * by Antonio Huete Jimenez <tuxillo@quantumachine.net> 6 * by Matthew Dillon <dillon@dragonflybsd.org> 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in 16 * the documentation and/or other materials provided with the 17 * distribution. 18 * 3. Neither the name of The DragonFly Project nor the names of its 19 * contributors may be used to endorse or promote products derived 20 * from this software without specific, prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 23 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 24 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 25 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 26 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 27 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 28 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 29 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 30 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 31 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 32 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 * 35 */ 36 37 #include <sys/vfsops.h> 38 #include <sys/mount.h> 39 #include <sys/types.h> 40 #include <sys/systm.h> 41 #include <sys/param.h> 42 #include <sys/module.h> 43 #include <sys/kernel.h> 44 #include <sys/malloc.h> 45 #include <sys/sysctl.h> 46 #include <sys/queue.h> 47 #include <sys/spinlock2.h> 48 #include <sys/ktr.h> 49 50 #include <string.h> 51 52 #include "dirfs.h" 53 54 MALLOC_DEFINE(M_DIRFS, "dirfs", "dirfs mount allocation"); 55 MALLOC_DEFINE(M_DIRFS_NODE, "dirfs nodes", "dirfs nodes memory allocation"); 56 MALLOC_DEFINE(M_DIRFS_MISC, "dirfs misc", "dirfs miscellaneous allocation"); 57 58 /* 59 * Kernel tracing facilities 60 */ 61 KTR_INFO_MASTER(dirfs); 62 63 KTR_INFO(KTR_DIRFS, dirfs, root, 20, 64 "DIRFS(root dnp=%p vnode=%p hostdir=%s fd=%d error=%d)", 65 dirfs_node_t dnp, struct vnode *vp, char *hostdir, int fd, int error); 66 67 KTR_INFO(KTR_DIRFS, dirfs, mount, 21, 68 "DIRFS(mount path=%s dmp=%p mp=%p error=%d)", 69 char *path, dirfs_mount_t dmp, struct mount *mp, int error); 70 71 KTR_INFO(KTR_DIRFS, dirfs, unmount, 22, 72 "DIRFS(unmount dmp=%p mp=%p error=%d)", 73 dirfs_mount_t dmp, struct mount *mp, int error); 74 75 /* System wide sysctl stuff */ 76 int debuglvl = 0; 77 int dirfs_fd_limit = 100; 78 int dirfs_fd_used = 0; 79 long passive_fd_list_miss = 0; 80 long passive_fd_list_hits = 0; 81 82 SYSCTL_NODE(_vfs, OID_AUTO, dirfs, CTLFLAG_RW, 0, 83 "dirfs filesystem for vkernels"); 84 SYSCTL_INT(_vfs_dirfs, OID_AUTO, debug, CTLFLAG_RW, 85 &debuglvl, 0, "dirfs debug level"); 86 SYSCTL_INT(_vfs_dirfs, OID_AUTO, fd_limit, CTLFLAG_RW, 87 &dirfs_fd_limit, 0, "Maximum number of passive nodes to cache"); 88 SYSCTL_INT(_vfs_dirfs, OID_AUTO, fd_used, CTLFLAG_RD, 89 &dirfs_fd_used, 0, "Current number of passive nodes cached"); 90 SYSCTL_LONG(_vfs_dirfs, OID_AUTO, passive_fd_list_miss, CTLFLAG_RD, 91 &passive_fd_list_miss, 0, "Passive fd list cache misses"); 92 SYSCTL_LONG(_vfs_dirfs, OID_AUTO, passive_fd_list_hits, CTLFLAG_RD, 93 &passive_fd_list_hits, 0, "Passive fd list cache misses"); 94 95 static int dirfs_statfs(struct mount *, struct statfs *, struct ucred *); 96 97 static int 98 dirfs_mount(struct mount *mp, char *path, caddr_t data, struct ucred *cred) 99 { 100 dirfs_mount_t dmp; 101 struct stat st; 102 size_t done, nlen; 103 int error; 104 105 dbg(1, "called\n"); 106 107 if (mp->mnt_flag & MNT_UPDATE) { 108 dmp = VFS_TO_DIRFS(mp); 109 if (dmp->dm_rdonly == 0 && (mp->mnt_flag & MNT_RDONLY)) { 110 /* XXX We should make sure all writes are synced */ 111 dmp->dm_rdonly = 1; 112 debug(2, "dirfs read-write -> read-only\n"); 113 } 114 115 if (dmp->dm_rdonly && (mp->mnt_kern_flag & MNTK_WANTRDWR)) { 116 debug(2, "dirfs read-only -> read-write\n"); 117 dmp->dm_rdonly = 0; 118 } 119 return 0; 120 } 121 122 dmp = kmalloc(sizeof(*dmp), M_DIRFS, M_WAITOK | M_ZERO); 123 mp->mnt_data = (qaddr_t)dmp; 124 dmp->dm_mount = mp; 125 126 error = copyinstr(data, &dmp->dm_path, MAXPATHLEN, &done); 127 if (error) { 128 /* Attempt to copy from kernel address */ 129 error = copystr(data, &dmp->dm_path, MAXPATHLEN, &done); 130 if (error) { 131 kfree(dmp, M_DIRFS); 132 goto failure; 133 } 134 } 135 136 /* Strip / character at the end to avoid problems */ 137 nlen = strnlen(dmp->dm_path, MAXPATHLEN); 138 if (dmp->dm_path[nlen-1] == '/') 139 dmp->dm_path[nlen-1] = 0; 140 141 /* Make sure host directory exists and it is indeed a directory. */ 142 if ((stat(dmp->dm_path, &st)) == 0) { 143 if (!S_ISDIR(st.st_mode)) { 144 kfree(dmp, M_DIRFS); 145 error = EINVAL; 146 goto failure; 147 } 148 } else { 149 error = errno; 150 goto failure; 151 } 152 153 lockinit(&dmp->dm_lock, "dfsmnt", 0, LK_CANRECURSE); 154 155 vfs_add_vnodeops(mp, &dirfs_vnode_vops, &mp->mnt_vn_norm_ops); 156 vfs_getnewfsid(mp); 157 158 /* Who is running the vkernel */ 159 dmp->dm_uid = getuid(); 160 dmp->dm_gid = getgid(); 161 162 TAILQ_INIT(&dmp->dm_fdlist); 163 RB_INIT(&dmp->dm_inotree); 164 165 kmalloc_raise_limit(M_DIRFS_NODE, 0); 166 167 dirfs_statfs(mp, &mp->mnt_stat, cred); 168 169 failure: 170 KTR_LOG(dirfs_mount, (dmp->dm_path) ? dmp->dm_path : "NULL", 171 dmp, mp, error); 172 173 return error; 174 } 175 176 static int 177 dirfs_unmount(struct mount *mp, int mntflags) 178 { 179 dirfs_mount_t dmp; 180 dirfs_node_t dnp; 181 int cnt; 182 int error; 183 184 dbg(1, "called\n"); 185 cnt = 0; 186 dmp = VFS_TO_DIRFS(mp); 187 188 error = vflush(mp, 0, 0); 189 if (error) 190 goto failure; 191 192 /* 193 * Clean up dm_fdlist. There should be no vnodes left so the 194 * only ref should be from the fdlist. 195 */ 196 while ((dnp = TAILQ_FIRST(&dmp->dm_fdlist)) != NULL) { 197 dirfs_node_setpassive(dmp, dnp, 0); 198 } 199 200 /* 201 * Cleanup root node. In the case the filesystem is mounted 202 * but no operation is done on it, there will be no call to 203 * VFS_ROOT() so better check dnp is not NULL before attempting 204 * to release it. 205 */ 206 dnp = dmp->dm_root; 207 if (dnp != NULL) { 208 dirfs_close_helper(dnp); 209 debug_node2(dnp); 210 dirfs_node_drop(dmp, dnp); /* last ref should free structure */ 211 } 212 kfree(dmp, M_DIRFS); 213 mp->mnt_data = (qaddr_t) 0; 214 215 failure: 216 KTR_LOG(dirfs_unmount, dmp, mp, error); 217 218 return error; 219 } 220 221 static int 222 dirfs_root(struct mount *mp, struct vnode **vpp) 223 { 224 dirfs_mount_t dmp; 225 dirfs_node_t dnp; 226 int fd; 227 int error; 228 229 dbg(1, "called\n"); 230 231 dmp = VFS_TO_DIRFS(mp); 232 KKASSERT(dmp != NULL); 233 234 if (dmp->dm_root == NULL) { 235 /* 236 * dm_root holds the root dirfs node. Allocate a new one since 237 * there is none. Also attempt to lstat(2) it, in order to set 238 * data for VOP_ACCESS() 239 */ 240 dnp = dirfs_node_alloc(mp); 241 error = dirfs_node_stat(DIRFS_NOFD, dmp->dm_path, dnp); 242 if (error != 0) { 243 dirfs_node_free(dmp, dnp); 244 return error; 245 } 246 dirfs_node_ref(dnp); /* leave inact for life of mount */ 247 248 /* Root inode's parent is NULL, used for verification */ 249 dnp->dn_parent = NULL; 250 dmp->dm_root = dnp; 251 dirfs_node_setflags(dnp, DIRFS_ROOT); 252 253 /* 254 * Maintain an open descriptor on the root dnp. The 255 * normal open/close/cache does not apply for the root 256 * so the descriptor is ALWAYS available. 257 */ 258 fd = open(dmp->dm_path, O_DIRECTORY); 259 if (fd == -1) { 260 dbg(9, "failed to open ROOT node\n"); 261 dirfs_free_vp(dmp, dnp); 262 dirfs_node_free(dmp, dnp); 263 return errno; 264 } 265 dnp->dn_fd = fd; 266 dnp->dn_type = VDIR; 267 } else { 268 dnp = dmp->dm_root; 269 } 270 271 /* 272 * Acquire the root vnode (dn_type already set above). This 273 * call will handle any races and return a locked vnode. 274 */ 275 dirfs_alloc_vp(mp, vpp, LK_CANRECURSE, dnp); 276 KTR_LOG(dirfs_root, dnp, *vpp, dmp->dm_path, dnp->dn_fd, error); 277 278 return 0; 279 } 280 281 static int 282 dirfs_fhtovp(struct mount *mp, struct vnode *rootvp, struct fid *fhp, struct vnode **vpp) 283 { 284 dbg(1, "called\n"); 285 286 return EOPNOTSUPP; 287 } 288 289 static int 290 dirfs_statfs(struct mount *mp, struct statfs *sbp, struct ucred *cred) 291 { 292 dirfs_mount_t dmp = VFS_TO_DIRFS(mp); 293 struct statfs st; 294 295 dbg(1, "called\n"); 296 297 if((statfs(dmp->dm_path, &st)) == -1) 298 return errno; 299 300 ksnprintf(st.f_mntfromname, MNAMELEN - 1, "dirfs@%s", dmp->dm_path); 301 bcopy(&st, sbp, sizeof(st)); 302 strlcpy(sbp->f_fstypename, mp->mnt_vfc->vfc_name, MFSNAMELEN); 303 dbg(5, "iosize = %zd\n", sbp->f_iosize); 304 305 return 0; 306 } 307 308 static int 309 dirfs_statvfs(struct mount *mp, struct statvfs *sbp, struct ucred *cred) 310 { 311 dirfs_mount_t dmp = VFS_TO_DIRFS(mp); 312 struct statvfs st; 313 314 dbg(1, "called\n"); 315 316 if ((statvfs(dmp->dm_path, &st)) == -1) 317 return errno; 318 319 bcopy(&st, sbp, sizeof(st)); 320 321 return 0; 322 } 323 324 static int 325 dirfs_vptofh(struct vnode *vp, struct fid *fhp) 326 { 327 dirfs_node_t dnp; 328 329 dnp = VP_TO_NODE(vp); 330 debug_node2(dnp); 331 dbg(1, "called\n"); 332 333 return EOPNOTSUPP; 334 } 335 336 static int 337 dirfs_checkexp(struct mount *mp, struct sockaddr *nam, int *exflagsp, 338 struct ucred **credanonp) 339 { 340 dbg(1, "called\n"); 341 342 return EOPNOTSUPP; 343 } 344 345 static struct vfsops dirfs_vfsops = { 346 .vfs_mount = dirfs_mount, 347 .vfs_unmount = dirfs_unmount, 348 .vfs_root = dirfs_root, 349 .vfs_vget = vfs_stdvget, 350 .vfs_statfs = dirfs_statfs, 351 .vfs_statvfs = dirfs_statvfs, 352 .vfs_fhtovp = dirfs_fhtovp, 353 .vfs_vptofh = dirfs_vptofh, 354 .vfs_checkexp = dirfs_checkexp 355 }; 356 357 VFS_SET(dirfs_vfsops, dirfs, 0); 358 MODULE_VERSION(dirfs, 1); 359