| 1 | /* |
| 2 | * Copyright (c) 1999, 2000 Boris Popov |
| 3 | * All rights reserved. |
| 4 | * |
| 5 | * Redistribution and use in source and binary forms, with or without |
| 6 | * modification, are permitted provided that the following conditions |
| 7 | * are met: |
| 8 | * 1. Redistributions of source code must retain the above copyright |
| 9 | * notice, this list of conditions and the following disclaimer. |
| 10 | * 2. Redistributions in binary form must reproduce the above copyright |
| 11 | * notice, this list of conditions and the following disclaimer in the |
| 12 | * documentation and/or other materials provided with the distribution. |
| 13 | * 3. All advertising materials mentioning features or use of this software |
| 14 | * must display the following acknowledgement: |
| 15 | * This product includes software developed by Boris Popov. |
| 16 | * 4. Neither the name of the author nor the names of any co-contributors |
| 17 | * may be used to endorse or promote products derived from this software |
| 18 | * without specific prior written permission. |
| 19 | * |
| 20 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
| 21 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| 23 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
| 24 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| 25 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
| 26 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| 27 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| 28 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| 29 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| 30 | * SUCH DAMAGE. |
| 31 | * |
| 32 | * $FreeBSD: src/sys/nwfs/nwfs_vfsops.c,v 1.6.2.6 2001/10/25 19:18:54 dillon Exp $ |
| 33 | * $DragonFly: src/sys/vfs/nwfs/nwfs_vfsops.c,v 1.30 2008/01/06 16:55:53 swildner Exp $ |
| 34 | */ |
| 35 | #include "opt_ncp.h" |
| 36 | #ifndef NCP |
| 37 | #error "NWFS requires NCP protocol" |
| 38 | #endif |
| 39 | |
| 40 | #include <sys/param.h> |
| 41 | #include <sys/systm.h> |
| 42 | #include <sys/proc.h> |
| 43 | #include <sys/kernel.h> |
| 44 | #include <sys/sysctl.h> |
| 45 | #include <sys/vnode.h> |
| 46 | #include <sys/mount.h> |
| 47 | #include <sys/stat.h> |
| 48 | #include <sys/malloc.h> |
| 49 | #include <sys/buf.h> |
| 50 | |
| 51 | #include <netproto/ncp/ncp.h> |
| 52 | #include <netproto/ncp/ncp_conn.h> |
| 53 | #include <netproto/ncp/ncp_subr.h> |
| 54 | #include <netproto/ncp/ncp_ncp.h> |
| 55 | #include <netproto/ncp/ncp_nls.h> |
| 56 | |
| 57 | #include "nwfs.h" |
| 58 | #include "nwfs_node.h" |
| 59 | #include "nwfs_subr.h" |
| 60 | |
| 61 | extern struct vop_ops nwfs_vnode_vops; |
| 62 | |
| 63 | int nwfs_debuglevel = 0; |
| 64 | |
| 65 | static int nwfs_version = NWFS_VERSION; |
| 66 | |
| 67 | SYSCTL_DECL(_vfs_nwfs); |
| 68 | SYSCTL_NODE(_vfs, OID_AUTO, nwfs, CTLFLAG_RW, 0, "Netware file system"); |
| 69 | SYSCTL_INT(_vfs_nwfs, OID_AUTO, version, CTLFLAG_RD, &nwfs_version, 0, ""); |
| 70 | SYSCTL_INT(_vfs_nwfs, OID_AUTO, debuglevel, CTLFLAG_RW, &nwfs_debuglevel, 0, ""); |
| 71 | |
| 72 | static int nwfs_mount(struct mount *, char *, caddr_t, struct ucred *); |
| 73 | static int nwfs_root(struct mount *, struct vnode **); |
| 74 | static int nwfs_statfs(struct mount *, struct statfs *, struct ucred *); |
| 75 | static int nwfs_sync(struct mount *, int); |
| 76 | static int nwfs_unmount(struct mount *, int); |
| 77 | static int nwfs_init(struct vfsconf *vfsp); |
| 78 | static int nwfs_uninit(struct vfsconf *vfsp); |
| 79 | |
| 80 | static struct vfsops nwfs_vfsops = { |
| 81 | .vfs_mount = nwfs_mount, |
| 82 | .vfs_unmount = nwfs_unmount, |
| 83 | .vfs_root = nwfs_root, |
| 84 | .vfs_statfs = nwfs_statfs, |
| 85 | .vfs_sync = nwfs_sync, |
| 86 | .vfs_init = nwfs_init, |
| 87 | .vfs_uninit = nwfs_uninit |
| 88 | }; |
| 89 | |
| 90 | |
| 91 | VFS_SET(nwfs_vfsops, nwfs, VFCF_NETWORK); |
| 92 | |
| 93 | int nwfs_pbuf_freecnt = -1; /* start out unlimited */ |
| 94 | static int nwfsid = 1; |
| 95 | |
| 96 | static int |
| 97 | nwfs_initnls(struct nwmount *nmp) { |
| 98 | char *pc, *pe; |
| 99 | int error = 0; |
| 100 | #define COPY_TABLE(t,d) { \ |
| 101 | if (t) { \ |
| 102 | error = copyin((t), pc, 256); \ |
| 103 | if (error) break; \ |
| 104 | } else \ |
| 105 | bcopy(d, pc, 256); \ |
| 106 | (t) = pc; pc += 256; \ |
| 107 | } |
| 108 | |
| 109 | nmp->m.nls.opt |= NWHP_NLS | NWHP_DOS; |
| 110 | if ((nmp->m.flags & NWFS_MOUNT_HAVE_NLS) == 0) { |
| 111 | nmp->m.nls.to_lower = ncp_defnls.to_lower; |
| 112 | nmp->m.nls.to_upper = ncp_defnls.to_upper; |
| 113 | nmp->m.nls.n2u = ncp_defnls.n2u; |
| 114 | nmp->m.nls.u2n = ncp_defnls.u2n; |
| 115 | return 0; |
| 116 | } |
| 117 | MALLOC(pe, char *, 256 * 4, M_NWFSDATA, M_WAITOK); |
| 118 | pc = pe; |
| 119 | do { |
| 120 | COPY_TABLE(nmp->m.nls.to_lower, ncp_defnls.to_lower); |
| 121 | COPY_TABLE(nmp->m.nls.to_upper, ncp_defnls.to_upper); |
| 122 | COPY_TABLE(nmp->m.nls.n2u, ncp_defnls.n2u); |
| 123 | COPY_TABLE(nmp->m.nls.u2n, ncp_defnls.u2n); |
| 124 | } while(0); |
| 125 | if (error) { |
| 126 | kfree(pe, M_NWFSDATA); |
| 127 | return error; |
| 128 | } |
| 129 | return 0; |
| 130 | } |
| 131 | /* |
| 132 | * mp - path - addr in user space of mount point (ie /usr or whatever) |
| 133 | * data - addr in user space of mount params |
| 134 | */ |
| 135 | static int |
| 136 | nwfs_mount(struct mount *mp, char *path, caddr_t data, struct ucred *cred) |
| 137 | { |
| 138 | struct nwfs_args args; /* will hold data from mount request */ |
| 139 | int error; |
| 140 | struct nwmount *nmp = NULL; |
| 141 | struct ncp_conn *conn = NULL; |
| 142 | struct ncp_handle *handle = NULL; |
| 143 | struct vnode *vp; |
| 144 | char *pc,*pe; |
| 145 | |
| 146 | if (data == NULL) { |
| 147 | nwfs_printf("missing data argument\n"); |
| 148 | return 1; |
| 149 | } |
| 150 | if (mp->mnt_flag & MNT_UPDATE) { |
| 151 | nwfs_printf("MNT_UPDATE not implemented"); |
| 152 | return (EOPNOTSUPP); |
| 153 | } |
| 154 | error = copyin(data, (caddr_t)&args, sizeof(struct nwfs_args)); |
| 155 | if (error) |
| 156 | return (error); |
| 157 | if (args.version != NWFS_VERSION) { |
| 158 | nwfs_printf("mount version mismatch: kernel=%d, mount=%d\n",NWFS_VERSION,args.version); |
| 159 | return (1); |
| 160 | } |
| 161 | error = ncp_conn_getbyref(args.connRef,curthread,cred,NCPM_EXECUTE,&conn); |
| 162 | if (error) { |
| 163 | nwfs_printf("invalid connection reference %d\n",args.connRef); |
| 164 | return (error); |
| 165 | } |
| 166 | error = ncp_conn_gethandle(conn, NULL, &handle); |
| 167 | if (error) { |
| 168 | nwfs_printf("can't get connection handle\n"); |
| 169 | return (error); |
| 170 | } |
| 171 | ncp_conn_unlock(conn,curthread); /* we keep the ref */ |
| 172 | mp->mnt_stat.f_iosize = conn->buffer_size; |
| 173 | /* We must malloc our own mount info */ |
| 174 | MALLOC(nmp,struct nwmount *,sizeof(struct nwmount),M_NWFSDATA, M_WAITOK|M_USE_RESERVE|M_ZERO); |
| 175 | mp->mnt_data = (qaddr_t)nmp; |
| 176 | nmp->connh = handle; |
| 177 | nmp->n_root = NULL; |
| 178 | nmp->n_id = nwfsid++; |
| 179 | nmp->m = args; |
| 180 | nmp->m.file_mode = (nmp->m.file_mode & |
| 181 | (S_IRWXU|S_IRWXG|S_IRWXO)) | S_IFREG; |
| 182 | nmp->m.dir_mode = (nmp->m.dir_mode & |
| 183 | (S_IRWXU|S_IRWXG|S_IRWXO)) | S_IFDIR; |
| 184 | if ((error = nwfs_initnls(nmp)) != 0) goto bad; |
| 185 | pc = mp->mnt_stat.f_mntfromname; |
| 186 | pe = pc+sizeof(mp->mnt_stat.f_mntfromname); |
| 187 | bzero(pc, MNAMELEN); |
| 188 | *(pc++) = '/'; |
| 189 | pc = index(strncpy(pc, conn->li.server, pe-pc-2),0); |
| 190 | if (pc < pe-1) { |
| 191 | *(pc++) = ':'; |
| 192 | pc=index(strncpy(pc, conn->li.user, pe-pc-2),0); |
| 193 | if (pc < pe-1) { |
| 194 | *(pc++) = '/'; |
| 195 | strncpy(pc, nmp->m.mounted_vol, pe-pc-2); |
| 196 | } |
| 197 | } |
| 198 | /* protect against invalid mount points */ |
| 199 | nmp->m.mount_point[sizeof(nmp->m.mount_point)-1] = '\0'; |
| 200 | |
| 201 | vfs_add_vnodeops(mp, &nwfs_vnode_vops, &mp->mnt_vn_norm_ops); |
| 202 | |
| 203 | vfs_getnewfsid(mp); |
| 204 | error = nwfs_root(mp, &vp); |
| 205 | if (error) |
| 206 | goto bad; |
| 207 | /* |
| 208 | * Lose the lock but keep the ref. |
| 209 | */ |
| 210 | vn_unlock(vp); |
| 211 | NCPVODEBUG("rootvp.vrefcnt=%d\n",vp->v_sysref.refcnt); |
| 212 | return error; |
| 213 | bad: |
| 214 | if (nmp) |
| 215 | kfree(nmp, M_NWFSDATA); |
| 216 | if (handle) |
| 217 | ncp_conn_puthandle(handle, NULL, 0); |
| 218 | return error; |
| 219 | } |
| 220 | |
| 221 | /* Unmount the filesystem described by mp. */ |
| 222 | static int |
| 223 | nwfs_unmount(struct mount *mp, int mntflags) |
| 224 | { |
| 225 | struct nwmount *nmp = VFSTONWFS(mp); |
| 226 | struct ncp_conn *conn; |
| 227 | int error, flags; |
| 228 | |
| 229 | NCPVODEBUG("nwfs_unmount: flags=%04x\n",mntflags); |
| 230 | flags = 0; |
| 231 | if (mntflags & MNT_FORCE) |
| 232 | flags |= FORCECLOSE; |
| 233 | /* There is 1 extra root vnode reference from nwfs_mount(). */ |
| 234 | error = vflush(mp, 1, flags); |
| 235 | if (error) |
| 236 | return (error); |
| 237 | conn = NWFSTOCONN(nmp); |
| 238 | ncp_conn_puthandle(nmp->connh,NULL,0); |
| 239 | if (ncp_conn_lock(conn, curthread, proc0.p_ucred, NCPM_WRITE | NCPM_EXECUTE) == 0) { |
| 240 | if(ncp_disconnect(conn)) |
| 241 | ncp_conn_unlock(conn, curthread); |
| 242 | } |
| 243 | mp->mnt_data = (qaddr_t)0; |
| 244 | if (nmp->m.flags & NWFS_MOUNT_HAVE_NLS) |
| 245 | kfree(nmp->m.nls.to_lower, M_NWFSDATA); |
| 246 | kfree(nmp, M_NWFSDATA); |
| 247 | mp->mnt_flag &= ~MNT_LOCAL; |
| 248 | return (error); |
| 249 | } |
| 250 | |
| 251 | /* Return locked vnode to root of a filesystem */ |
| 252 | static int |
| 253 | nwfs_root(struct mount *mp, struct vnode **vpp) |
| 254 | { |
| 255 | struct vnode *vp; |
| 256 | struct nwmount *nmp; |
| 257 | struct nwnode *np; |
| 258 | struct ncp_conn *conn; |
| 259 | struct nw_entry_info fattr; |
| 260 | struct thread *td = curthread; /* XXX */ |
| 261 | struct ucred *cred; |
| 262 | int error, nsf, opt; |
| 263 | u_char vol; |
| 264 | |
| 265 | KKASSERT(td->td_proc); |
| 266 | cred = td->td_proc->p_ucred; |
| 267 | |
| 268 | nmp = VFSTONWFS(mp); |
| 269 | conn = NWFSTOCONN(nmp); |
| 270 | if (nmp->n_root) { |
| 271 | *vpp = NWTOV(nmp->n_root); |
| 272 | while (vget(*vpp, LK_EXCLUSIVE) != 0) /* XXX */ |
| 273 | ; |
| 274 | return 0; |
| 275 | } |
| 276 | error = ncp_lookup_volume(conn, nmp->m.mounted_vol, &vol, |
| 277 | &nmp->n_rootent.f_id, td, cred); |
| 278 | if (error) |
| 279 | return ENOENT; |
| 280 | nmp->n_volume = vol; |
| 281 | error = ncp_get_namespaces(conn, vol, &nsf, td, cred); |
| 282 | if (error) |
| 283 | return ENOENT; |
| 284 | if (nsf & NW_NSB_OS2) { |
| 285 | NCPVODEBUG("volume %s has os2 namespace\n",nmp->m.mounted_vol); |
| 286 | if ((nmp->m.flags & NWFS_MOUNT_NO_OS2) == 0) { |
| 287 | nmp->name_space = NW_NS_OS2; |
| 288 | nmp->m.nls.opt &= ~NWHP_DOS; |
| 289 | } |
| 290 | } |
| 291 | opt = nmp->m.nls.opt; |
| 292 | nsf = opt & (NWHP_UPPER | NWHP_LOWER); |
| 293 | if (opt & NWHP_DOS) { |
| 294 | if (nsf == (NWHP_UPPER | NWHP_LOWER)) { |
| 295 | nmp->m.nls.opt &= ~(NWHP_LOWER | NWHP_UPPER); |
| 296 | } else if (nsf == 0) { |
| 297 | nmp->m.nls.opt |= NWHP_LOWER; |
| 298 | } |
| 299 | } else { |
| 300 | if (nsf == (NWHP_UPPER | NWHP_LOWER)) { |
| 301 | nmp->m.nls.opt &= ~(NWHP_LOWER | NWHP_UPPER); |
| 302 | } |
| 303 | } |
| 304 | if (nmp->m.root_path[0]) { |
| 305 | nmp->m.root_path[0]--; |
| 306 | error = ncp_obtain_info(nmp, nmp->n_rootent.f_id, |
| 307 | -nmp->m.root_path[0], nmp->m.root_path, &fattr, td, cred); |
| 308 | if (error) { |
| 309 | NCPFATAL("Invalid root path specified\n"); |
| 310 | return ENOENT; |
| 311 | } |
| 312 | nmp->n_rootent.f_parent = fattr.dirEntNum; |
| 313 | nmp->m.root_path[0]++; |
| 314 | error = ncp_obtain_info(nmp, nmp->n_rootent.f_id, |
| 315 | -nmp->m.root_path[0], nmp->m.root_path, &fattr, td, cred); |
| 316 | if (error) { |
| 317 | NCPFATAL("Invalid root path specified\n"); |
| 318 | return ENOENT; |
| 319 | } |
| 320 | nmp->n_rootent.f_id = fattr.dirEntNum; |
| 321 | } else { |
| 322 | error = ncp_obtain_info(nmp, nmp->n_rootent.f_id, |
| 323 | 0, NULL, &fattr, td, cred); |
| 324 | if (error) { |
| 325 | NCPFATAL("Can't obtain volume info\n"); |
| 326 | return ENOENT; |
| 327 | } |
| 328 | fattr.nameLen = strlen(strcpy(fattr.entryName, NWFS_ROOTVOL)); |
| 329 | nmp->n_rootent.f_parent = nmp->n_rootent.f_id; |
| 330 | } |
| 331 | error = nwfs_nget(mp, nmp->n_rootent, &fattr, NULL, &vp); |
| 332 | if (error) |
| 333 | return (error); |
| 334 | vp->v_flag |= VROOT; |
| 335 | np = VTONW(vp); |
| 336 | if (nmp->m.root_path[0] == 0) |
| 337 | np->n_flag |= NVOLUME; |
| 338 | nmp->n_root = np; |
| 339 | /* error = VOP_GETATTR(vp, &vattr); |
| 340 | if (error) { |
| 341 | vput(vp); |
| 342 | NCPFATAL("Can't get root directory entry\n"); |
| 343 | return error; |
| 344 | }*/ |
| 345 | *vpp = vp; |
| 346 | return (0); |
| 347 | } |
| 348 | |
| 349 | /*ARGSUSED*/ |
| 350 | int |
| 351 | nwfs_init(struct vfsconf *vfsp) |
| 352 | { |
| 353 | #ifndef SMP |
| 354 | int name[2]; |
| 355 | int ncpu, error; |
| 356 | size_t olen, plen; |
| 357 | |
| 358 | name[0] = CTL_HW; |
| 359 | name[1] = HW_NCPU; |
| 360 | error = kernel_sysctl(name, 2, &ncpu, &olen, NULL, 0, &plen); |
| 361 | if (error == 0 && ncpu > 1) |
| 362 | kprintf("warning: nwfs module compiled without SMP support."); |
| 363 | #endif |
| 364 | nwfs_hash_init(); |
| 365 | nwfs_pbuf_freecnt = nswbuf / 2 + 1; |
| 366 | NCPVODEBUG("always happy to load!\n"); |
| 367 | return (0); |
| 368 | } |
| 369 | |
| 370 | /*ARGSUSED*/ |
| 371 | int |
| 372 | nwfs_uninit(struct vfsconf *vfsp) |
| 373 | { |
| 374 | |
| 375 | nwfs_hash_free(); |
| 376 | NCPVODEBUG("unloaded\n"); |
| 377 | return (0); |
| 378 | } |
| 379 | |
| 380 | /* |
| 381 | * nwfs_statfs call |
| 382 | */ |
| 383 | int |
| 384 | nwfs_statfs(struct mount *mp, struct statfs *sbp, struct ucred *cred) |
| 385 | { |
| 386 | struct nwmount *nmp = VFSTONWFS(mp); |
| 387 | int error = 0, secsize; |
| 388 | struct nwnode *np = nmp->n_root; |
| 389 | struct ncp_volume_info vi; |
| 390 | |
| 391 | if (np == NULL) return EINVAL; |
| 392 | error = ncp_get_volume_info_with_number(NWFSTOCONN(nmp), nmp->n_volume, |
| 393 | &vi, curthread, cred); |
| 394 | if (error) return error; |
| 395 | secsize = 512; /* XXX how to get real value ??? */ |
| 396 | sbp->f_spare2=0; /* placeholder */ |
| 397 | /* fundamental file system block size */ |
| 398 | sbp->f_bsize = vi.sectors_per_block*secsize; |
| 399 | /* optimal transfer block size */ |
| 400 | sbp->f_iosize = NWFSTOCONN(nmp)->buffer_size; |
| 401 | /* total data blocks in file system */ |
| 402 | sbp->f_blocks= vi.total_blocks; |
| 403 | /* free blocks in fs */ |
| 404 | sbp->f_bfree = vi.free_blocks + vi.purgeable_blocks; |
| 405 | /* free blocks avail to non-superuser */ |
| 406 | sbp->f_bavail= vi.free_blocks+vi.purgeable_blocks; |
| 407 | /* total file nodes in file system */ |
| 408 | sbp->f_files = vi.total_dir_entries; |
| 409 | /* free file nodes in fs */ |
| 410 | sbp->f_ffree = vi.available_dir_entries; |
| 411 | sbp->f_flags = 0; /* copy of mount exported flags */ |
| 412 | if (sbp != &mp->mnt_stat) { |
| 413 | sbp->f_fsid = mp->mnt_stat.f_fsid; /* file system id */ |
| 414 | sbp->f_owner = mp->mnt_stat.f_owner; /* user that mounted the filesystem */ |
| 415 | sbp->f_type = mp->mnt_vfc->vfc_typenum; /* type of filesystem */ |
| 416 | bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN); |
| 417 | } |
| 418 | strncpy(sbp->f_fstypename, mp->mnt_vfc->vfc_name, MFSNAMELEN); |
| 419 | return 0; |
| 420 | } |
| 421 | |
| 422 | /* |
| 423 | * Flush out the buffer cache |
| 424 | */ |
| 425 | /* ARGSUSED */ |
| 426 | static int |
| 427 | nwfs_sync(struct mount *mp, int waitfor) |
| 428 | { |
| 429 | struct vnode *vp; |
| 430 | int error, allerror = 0; |
| 431 | /* |
| 432 | * Force stale buffer cache information to be flushed. |
| 433 | */ |
| 434 | loop: |
| 435 | for (vp = TAILQ_FIRST(&mp->mnt_nvnodelist); |
| 436 | vp != NULL; |
| 437 | vp = TAILQ_NEXT(vp, v_nmntvnodes)) { |
| 438 | /* |
| 439 | * If the vnode that we are about to sync is no longer |
| 440 | * associated with this mount point, start over. |
| 441 | */ |
| 442 | if (vp->v_mount != mp) |
| 443 | goto loop; |
| 444 | if (vn_islocked(vp) || RB_EMPTY(&vp->v_rbdirty_tree) || |
| 445 | waitfor == MNT_LAZY) |
| 446 | continue; |
| 447 | if (vget(vp, LK_EXCLUSIVE)) |
| 448 | goto loop; |
| 449 | /* XXX vp may not be retained */ |
| 450 | error = VOP_FSYNC(vp, waitfor); |
| 451 | if (error) |
| 452 | allerror = error; |
| 453 | vput(vp); |
| 454 | } |
| 455 | return (allerror); |
| 456 | } |