1 /*- 2 * Copyright (c) 2017 Juniper Networks, Inc. 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 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 * 25 */ 26 /*- 27 * 9P filesystem subroutines. This file consists of all the Non VFS subroutines. 28 * It contains all of the functions related to the driver submission which form 29 * the upper layer i.e, p9fs driver. This will interact with the client to make 30 * sure we have correct API calls in the header. 31 */ 32 33 #include <sys/cdefs.h> 34 #include <sys/systm.h> 35 #include <sys/limits.h> 36 #include <sys/mount.h> 37 #include <sys/sysctl.h> 38 #include <sys/vnode.h> 39 40 #include "p9fs_proto.h" 41 42 #include <fs/p9fs/p9_client.h> 43 #include <fs/p9fs/p9_debug.h> 44 #include <fs/p9fs/p9_protocol.h> 45 #include <fs/p9fs/p9fs.h> 46 47 int 48 p9fs_proto_dotl(struct p9fs_session *vses) 49 { 50 51 return (vses->flags & P9FS_PROTO_2000L); 52 } 53 54 /* Initialize a p9fs session */ 55 struct p9_fid * 56 p9fs_init_session(struct mount *mp, int *error) 57 { 58 struct p9fs_session *vses; 59 struct p9fs_mount *virtmp; 60 struct p9_fid *fid; 61 char *access; 62 63 virtmp = VFSTOP9(mp); 64 vses = &virtmp->p9fs_session; 65 vses->uid = P9_NONUNAME; 66 vses->uname = P9_DEFUNAME; 67 vses->aname = P9_DEFANAME; 68 69 /* 70 * Create the client structure. Call into the driver to create 71 * driver structures for the actual IO transfer. 72 */ 73 vses->clnt = p9_client_create(mp, error, virtmp->mount_tag); 74 75 if (vses->clnt == NULL) { 76 P9_DEBUG(ERROR, "%s: p9_client_create failed\n", __func__); 77 return (NULL); 78 } 79 /* 80 * Find the client version and cache the copy. We will use this copy 81 * throughout FS layer. 82 */ 83 if (p9_is_proto_dotl(vses->clnt)) 84 vses->flags |= P9FS_PROTO_2000L; 85 else if (p9_is_proto_dotu(vses->clnt)) 86 vses->flags |= P9FS_PROTO_2000U; 87 88 /* Set the access mode */ 89 access = vfs_getopts(mp->mnt_optnew, "access", error); 90 if (access == NULL) 91 vses->flags |= P9_ACCESS_USER; 92 else if (!strcmp(access, "any")) 93 vses->flags |= P9_ACCESS_ANY; 94 else if (!strcmp(access, "single")) 95 vses->flags |= P9_ACCESS_SINGLE; 96 else if (!strcmp(access, "user")) 97 vses->flags |= P9_ACCESS_USER; 98 else { 99 P9_DEBUG(ERROR, "%s: unknown access mode\n", __func__); 100 *error = EINVAL; 101 goto out; 102 } 103 104 *error = 0; 105 /* Attach with the backend host*/ 106 fid = p9_client_attach(vses->clnt, NULL, vses->uname, P9_NONUNAME, 107 vses->aname, error); 108 vses->mnt_fid = fid; 109 110 if (*error != 0) { 111 P9_DEBUG(ERROR, "%s: attach failed: %d\n", __func__, *error); 112 goto out; 113 } 114 P9_DEBUG(SUBR, "%s: attach successful fid :%p\n", __func__, fid); 115 fid->uid = vses->uid; 116 117 /* initialize the node list for the session */ 118 STAILQ_INIT(&vses->virt_node_list); 119 P9FS_LOCK_INIT(vses); 120 121 P9_DEBUG(SUBR, "%s: INIT session successful\n", __func__); 122 123 return (fid); 124 out: 125 p9_client_destroy(vses->clnt); 126 return (NULL); 127 } 128 129 /* Begin to terminate a session */ 130 void 131 p9fs_prepare_to_close(struct mount *mp) 132 { 133 struct p9fs_session *vses; 134 struct p9fs_mount *vmp; 135 struct p9fs_node *np, *pnp, *tmp; 136 137 vmp = VFSTOP9(mp); 138 vses = &vmp->p9fs_session; 139 140 /* break the node->parent references */ 141 STAILQ_FOREACH_SAFE(np, &vses->virt_node_list, p9fs_node_next, tmp) { 142 if (np->parent && np->parent != np) { 143 pnp = np->parent; 144 np->parent = NULL; 145 vrele(P9FS_NTOV(pnp)); 146 } 147 } 148 149 /* We are about to teardown, we dont allow anything other than clunk after this.*/ 150 p9_client_begin_disconnect(vses->clnt); 151 } 152 153 /* Shutdown a session */ 154 void 155 p9fs_complete_close(struct mount *mp) 156 { 157 struct p9fs_session *vses; 158 struct p9fs_mount *vmp; 159 160 vmp = VFSTOP9(mp); 161 vses = &vmp->p9fs_session; 162 163 /* Finish the close*/ 164 p9_client_disconnect(vses->clnt); 165 } 166 167 168 /* Call from unmount. Close the session. */ 169 void 170 p9fs_close_session(struct mount *mp) 171 { 172 struct p9fs_session *vses; 173 struct p9fs_mount *vmp; 174 175 vmp = VFSTOP9(mp); 176 vses = &vmp->p9fs_session; 177 178 p9fs_complete_close(mp); 179 /* Clean up the clnt structure. */ 180 p9_client_destroy(vses->clnt); 181 P9FS_LOCK_DESTROY(vses); 182 P9_DEBUG(SUBR, "%s: Clean close session .\n", __func__); 183 } 184 185 /* 186 * Remove all the fids of a particular type from a p9fs node 187 * as well as destroy/clunk them. 188 */ 189 void 190 p9fs_fid_remove_all(struct p9fs_node *np, int leave_ofids) 191 { 192 struct p9_fid *fid, *tfid; 193 194 STAILQ_FOREACH_SAFE(fid, &np->vfid_list, fid_next, tfid) { 195 STAILQ_REMOVE(&np->vfid_list, fid, p9_fid, fid_next); 196 p9_client_clunk(fid); 197 } 198 199 if (!leave_ofids) { 200 STAILQ_FOREACH_SAFE(fid, &np->vofid_list, fid_next, tfid) { 201 STAILQ_REMOVE(&np->vofid_list, fid, p9_fid, fid_next); 202 p9_client_clunk(fid); 203 } 204 } 205 } 206 207 208 /* Remove a fid from its corresponding fid list */ 209 void 210 p9fs_fid_remove(struct p9fs_node *np, struct p9_fid *fid, int fid_type) 211 { 212 213 switch (fid_type) { 214 case VFID: 215 P9FS_VFID_LOCK(np); 216 STAILQ_REMOVE(&np->vfid_list, fid, p9_fid, fid_next); 217 P9FS_VFID_UNLOCK(np); 218 break; 219 case VOFID: 220 P9FS_VOFID_LOCK(np); 221 STAILQ_REMOVE(&np->vofid_list, fid, p9_fid, fid_next); 222 P9FS_VOFID_UNLOCK(np); 223 break; 224 } 225 } 226 227 /* Add a fid to the corresponding fid list */ 228 void 229 p9fs_fid_add(struct p9fs_node *np, struct p9_fid *fid, int fid_type) 230 { 231 232 switch (fid_type) { 233 case VFID: 234 P9FS_VFID_LOCK(np); 235 STAILQ_INSERT_TAIL(&np->vfid_list, fid, fid_next); 236 P9FS_VFID_UNLOCK(np); 237 break; 238 case VOFID: 239 P9FS_VOFID_LOCK(np); 240 STAILQ_INSERT_TAIL(&np->vofid_list, fid, fid_next); 241 P9FS_VOFID_UNLOCK(np); 242 break; 243 } 244 } 245 246 /* Build the path from root to current directory */ 247 static int 248 p9fs_get_full_path(struct p9fs_node *np, char ***names) 249 { 250 int i, n; 251 struct p9fs_node *node; 252 char **wnames; 253 254 n = 0; 255 for (node = np ; (node != NULL) && !IS_ROOT(node) ; node = node->parent) 256 n++; 257 258 if (node == NULL) 259 return (0); 260 261 wnames = malloc(n * sizeof(char *), M_TEMP, M_ZERO|M_WAITOK); 262 263 for (i = n-1, node = np; i >= 0 ; i--, node = node->parent) 264 wnames[i] = node->inode.i_name; 265 266 *names = wnames; 267 return (n); 268 } 269 270 /* 271 * Return TRUE if this fid can be used for the requested mode. 272 */ 273 static int 274 p9fs_compatible_mode(struct p9_fid *fid, int mode) 275 { 276 /* 277 * Return TRUE for an exact match. For OREAD and OWRITE, allow 278 * existing ORDWR fids to match. Only check the low two bits 279 * of mode. 280 * 281 * TODO: figure out if this is correct for O_APPEND 282 */ 283 int fid_mode = fid->mode & 3; 284 if (fid_mode == mode) 285 return (TRUE); 286 if (fid_mode == P9PROTO_ORDWR) 287 return (mode == P9PROTO_OREAD || mode == P9PROTO_OWRITE); 288 return (FALSE); 289 } 290 291 /* 292 * Retrieve fid structure corresponding to a particular 293 * uid and fid type for a p9fs node 294 */ 295 static struct p9_fid * 296 p9fs_get_fid_from_uid(struct p9fs_node *np, uid_t uid, int fid_type, int mode) 297 { 298 struct p9_fid *fid; 299 300 switch (fid_type) { 301 case VFID: 302 P9FS_VFID_LOCK(np); 303 STAILQ_FOREACH(fid, &np->vfid_list, fid_next) { 304 if (fid->uid == uid) { 305 P9FS_VFID_UNLOCK(np); 306 return (fid); 307 } 308 } 309 P9FS_VFID_UNLOCK(np); 310 break; 311 case VOFID: 312 P9FS_VOFID_LOCK(np); 313 STAILQ_FOREACH(fid, &np->vofid_list, fid_next) { 314 if (fid->uid == uid && p9fs_compatible_mode(fid, mode)) { 315 P9FS_VOFID_UNLOCK(np); 316 return (fid); 317 } 318 } 319 P9FS_VOFID_UNLOCK(np); 320 break; 321 } 322 323 return (NULL); 324 } 325 326 /* 327 * Function returns the fid sturcture for a file corresponding to current user id. 328 * First it searches in the fid list of the corresponding p9fs node. 329 * New fid will be created if not already present and added in the corresponding 330 * fid list in the p9fs node. 331 * If the user is not already attached then this will attach the user first 332 * and then create a new fid for this particular file by doing dir walk. 333 */ 334 struct p9_fid * 335 p9fs_get_fid(struct p9_client *clnt, struct p9fs_node *np, struct ucred *cred, 336 int fid_type, int mode, int *error) 337 { 338 uid_t uid; 339 struct p9_fid *fid, *oldfid; 340 struct p9fs_node *root; 341 struct p9fs_session *vses; 342 int i, l, clone; 343 char **wnames = NULL; 344 uint16_t nwnames; 345 346 oldfid = NULL; 347 vses = np->p9fs_ses; 348 349 if (vses->flags & P9_ACCESS_ANY) 350 uid = vses->uid; 351 else if (cred) 352 uid = cred->cr_uid; 353 else 354 uid = 0; 355 356 /* 357 * Search for the fid in corresponding fid list. 358 * We should return NULL for VOFID if it is not present in the list. 359 * Because VOFID should have been created during the file open. 360 * If VFID is not present in the list then we should create one. 361 */ 362 fid = p9fs_get_fid_from_uid(np, uid, fid_type, mode); 363 if (fid != NULL || fid_type == VOFID) 364 return (fid); 365 366 /* Check root if the user is attached */ 367 root = &np->p9fs_ses->rnp; 368 fid = p9fs_get_fid_from_uid(root, uid, fid_type, mode); 369 if(fid == NULL) { 370 /* Attach the user */ 371 fid = p9_client_attach(clnt, NULL, NULL, uid, 372 vses->aname, error); 373 if (*error != 0) 374 return (NULL); 375 p9fs_fid_add(root, fid, fid_type); 376 } 377 378 /* If we are looking for root then return it */ 379 if (IS_ROOT(np)) 380 return (fid); 381 382 /* Get full path from root to p9fs node */ 383 nwnames = p9fs_get_full_path(np, &wnames); 384 385 /* 386 * Could not get full path. 387 * If p9fs node is not deleted, parent should exist. 388 */ 389 KASSERT(nwnames != 0, ("%s: Directory of %s doesn't exist", __func__, np->inode.i_name)); 390 391 clone = 1; 392 i = 0; 393 while (i < nwnames) { 394 l = MIN(nwnames - i, P9_MAXWELEM); 395 396 fid = p9_client_walk(fid, l, wnames, clone, error); 397 if (*error != 0) { 398 if (oldfid) 399 p9_client_clunk(oldfid); 400 fid = NULL; 401 goto bail_out; 402 } 403 oldfid = fid; 404 clone = 0; 405 i += l ; 406 } 407 p9fs_fid_add(np, fid, fid_type); 408 bail_out: 409 free(wnames, M_TEMP); 410 return (fid); 411 } 412