├── README.md └── sys ├── dev └── virtio │ ├── 9pfs │ ├── virtfs.h │ ├── virtfs_proto.h │ ├── virtfs_subr.c │ ├── virtfs_vfops.c │ └── virtfs_vnops.c │ ├── 9pnet │ ├── client.c │ ├── protocol.c │ ├── trans_virtio.c │ ├── transport.h │ └── virtio_9p_config.h │ ├── virtio_fs_9p.h │ ├── virtio_fs_client.h │ └── virtio_fs_protocol.h └── modules └── virtio ├── 9pfs └── Makefile └── 9pnet └── Makefile /README.md: -------------------------------------------------------------------------------- 1 | virtfs-9p-kmod based on [code from Juniper](https://github.com/Juniper/virtfs/compare/jnpr/virtfs) 2 | 3 | Build tested on 12.x, 13.x and 14.x. Run tested on 12.x. 4 | 5 | Add these flags to bhyve: `-s 28,virtio-9p,9p=/some/local/path` 6 | 7 | In the bhyve VM, load the module and then: 8 | 9 | `mount -t virtfs -o trans=virtio 9p /some/vm/path` 10 | 11 | See also: 12 | 13 | * [9p review for bhyve](https://reviews.freebsd.org/D10335) 14 | * [lib9p](https://github.com/conclusiveeng/lib9p) 15 | * [9P](https://en.wikipedia.org/wiki/9P_(protocol)) 16 | * [9P protocol](http://9p.io/sys/man/5/INDEX.html) 17 | -------------------------------------------------------------------------------- /sys/dev/virtio/9pfs/virtfs.h: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 2017-2020 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 | /* This file has prototypes specific to the VirtFS file system */ 28 | 29 | #ifndef __VIRTFS__ 30 | #define __VIRTFS__ 31 | 32 | struct virtfs_session; 33 | 34 | /* 35 | * The in memory representation of the on disk inode. Save the current 36 | * fields to write it back later. 37 | */ 38 | struct virtfs_inode { 39 | /* Make it simple first, Add more fields later */ 40 | uint64_t i_size; /* size of the inode */ 41 | uint16_t i_type; /* type of inode */ 42 | uint32_t i_dev; /* type of device */ 43 | uint32_t i_mode; /* mode of the inode */ 44 | uint32_t i_atime; /* time of last access */ 45 | uint32_t i_mtime; /* time of last modification */ 46 | uint32_t i_ctime; /* time of last status change */ 47 | uint32_t i_atime_nsec; /* times of last access in nanoseconds resolution */ 48 | uint32_t i_mtime_nsec; /* time of last modification in nanoseconds resolution */ 49 | uint32_t i_ctime_nsec; /* time of last status change in nanoseconds resolution */ 50 | uint64_t i_length; 51 | char *i_name; /* inode name */ 52 | char *i_uid; /* inode user id */ 53 | char *i_gid; /* inode group id */ 54 | char *i_muid; 55 | char *i_extension; /* 9p2000.u extensions */ 56 | uid_t n_uid; /* 9p2000.u extensions */ 57 | gid_t n_gid; /* 9p2000.u extensions */ 58 | uid_t n_muid; /* 9p2000.u extensions */ 59 | /* bookkeeping info on the client. */ 60 | uint16_t i_links_count; /*number of references to the inode*/ 61 | uint64_t i_qid_path; /* using inode number for reference. */ 62 | uint64_t i_flags; 63 | uint64_t blksize; /* block size for file system */ 64 | uint64_t blocks; /* number of 512B blocks allocated */ 65 | uint64_t gen; /* reserved for future use */ 66 | uint64_t data_version; /* reserved for future use */ 67 | 68 | }; 69 | 70 | #define VIRTFS_VFID_MTX(_sc) (&(_sc)->vfid_mtx) 71 | #define VIRTFS_VFID_LOCK(_sc) mtx_lock(VIRTFS_VFID_MTX(_sc)) 72 | #define VIRTFS_VFID_UNLOCK(_sc) mtx_unlock(VIRTFS_VFID_MTX(_sc)) 73 | #define VIRTFS_VFID_LOCK_INIT(_sc) mtx_init(VIRTFS_VFID_MTX(_sc), \ 74 | "VFID List lock", NULL, MTX_DEF) 75 | #define VIRTFS_VFID_LOCK_DESTROY(_sc) mtx_destroy(VIRTFS_VFID_MTX(_sc)) 76 | 77 | #define VIRTFS_VOFID_MTX(_sc) (&(_sc)->vofid_mtx) 78 | #define VIRTFS_VOFID_LOCK(_sc) mtx_lock(VIRTFS_VOFID_MTX(_sc)) 79 | #define VIRTFS_VOFID_UNLOCK(_sc) mtx_unlock(VIRTFS_VOFID_MTX(_sc)) 80 | #define VIRTFS_VOFID_LOCK_INIT(_sc) mtx_init(VIRTFS_VOFID_MTX(_sc), \ 81 | "VOFID List lock", NULL, MTX_DEF) 82 | #define VIRTFS_VOFID_LOCK_DESTROY(_sc) mtx_destroy(VIRTFS_VOFID_MTX(_sc)) 83 | 84 | #define VFID 0x01 85 | #define VOFID 0x02 86 | 87 | /* A Plan9 node. */ 88 | struct virtfs_node { 89 | STAILQ_HEAD( ,p9_fid) vfid_list; /* vfid related to uid */ 90 | struct mtx vfid_mtx; /* mutex for vfid list */ 91 | STAILQ_HEAD( ,p9_fid) vofid_list; /* vofid related to uid */ 92 | struct mtx vofid_mtx; /* mutex for vofid list */ 93 | struct virtfs_node *parent; /* pointer to parent VirtFS node */ 94 | struct virtfs_qid vqid; /* the server qid, will be from the host */ 95 | struct vnode *v_node; /* vnode for this fs_node. */ 96 | struct virtfs_inode inode; /* in memory representation of ondisk information*/ 97 | struct virtfs_session *virtfs_ses; /* Session_ptr for this node */ 98 | STAILQ_ENTRY(virtfs_node) virtfs_node_next; 99 | uint64_t flags; 100 | }; 101 | 102 | #define VIRTFS_VTON(vp) ((vp)->v_data) 103 | #define VIRTFS_NTOV(node) ((node)->v_node) 104 | #define VFSTOP9(mp) ((mp)->mnt_data) 105 | #define QEMU_DIRENTRY_SZ 25 106 | #define VIRTFS_NODE_MODIFIED 0x1 /* indicating file change */ 107 | #define VIRTFS_ROOT 0x2 /* indicating root VirtFS node */ 108 | #define VIRTFS_NODE_DELETED 0x4 /* indicating file or directory delete */ 109 | #define VIRTFS_NODE_IN_SESSION 0x8 /* virtfs_node is in the session - virt_node_list */ 110 | #define IS_ROOT(node) (node->flags & VIRTFS_ROOT) 111 | 112 | #define VIRTFS_SET_LINKS(inode) do { \ 113 | (inode)->i_links_count = 1; \ 114 | } while (0) \ 115 | 116 | #define VIRTFS_INCR_LINKS(inode) do { \ 117 | (inode)->i_links_count++; \ 118 | } while (0) \ 119 | 120 | #define VIRTFS_DECR_LINKS(inode) do { \ 121 | (inode)->i_links_count--; \ 122 | } while (0) \ 123 | 124 | #define VIRTFS_CLR_LINKS(inode) do { \ 125 | (inode)->i_links_count = 0; \ 126 | } while (0) \ 127 | 128 | #define VIRTFS_MTX(_sc) (&(_sc)->virtfs_mtx) 129 | #define VIRTFS_LOCK(_sc) mtx_lock(VIRTFS_MTX(_sc)) 130 | #define VIRTFS_UNLOCK(_sc) mtx_unlock(VIRTFS_MTX(_sc)) 131 | #define VIRTFS_LOCK_INIT(_sc) mtx_init(VIRTFS_MTX(_sc), \ 132 | "VIRTFS session chain lock", NULL, MTX_DEF) 133 | #define VIRTFS_LOCK_DESTROY(_sc) mtx_destroy(VIRTFS_MTX(_sc)) 134 | 135 | /* Session structure for the FS */ 136 | struct virtfs_session { 137 | unsigned char flags; /* these flags for the session */ 138 | struct mount *virtfs_mount; /* mount point */ 139 | struct virtfs_node rnp; /* root VirtFS node for this session */ 140 | uid_t uid; /* the uid that has access */ 141 | const char *uname; /* user name to mount as */ 142 | const char *aname; /* name of remote file tree being mounted */ 143 | struct p9_client *clnt; /* 9p client */ 144 | struct mtx virtfs_mtx; /* mutex used for guarding the chain.*/ 145 | STAILQ_HEAD( ,virtfs_node) virt_node_list; /* list of VirtFS nodes in this session*/ 146 | struct p9_fid *mnt_fid; /* to save nobody 's fid for unmounting as root user */ 147 | }; 148 | 149 | typedef u_quad_t (*virtfs_filesize2bytes_t)(uint64_t filesize, uint64_t bsize); 150 | struct virtfs_mount { 151 | struct virtfs_session virtfs_session; /* per instance session information */ 152 | struct mount *virtfs_mountp; /* mount point */ 153 | virtfs_filesize2bytes_t virtfs_filesize2bytes; /* file size in bytes */ 154 | int mount_tag_len; /* length of the mount tag */ 155 | char *mount_tag; /* mount tag used */ 156 | }; 157 | 158 | /* All session flags based on 9p versions */ 159 | enum virt_session_flags { 160 | VIRTFS_PROTO_2000U = 0x01, 161 | VIRTFS_PROTO_2000L = 0x02, 162 | }; 163 | 164 | /* Session access flags */ 165 | #define P9_ACCESS_ANY 0x04 /* single attach for all users */ 166 | #define P9_ACCESS_SINGLE 0x08 /* access to only the user who mounts */ 167 | #define P9_ACCESS_USER 0x10 /* new attach established for every user */ 168 | #define P9_ACCESS_MASK (P9_ACCESS_ANY|P9_ACCESS_SINGLE|P9_ACCESS_USER) 169 | 170 | u_quad_t virtfs_round_filesize_to_bytes(uint64_t filesize, uint64_t bsize); 171 | u_quad_t virtfs_pow2_filesize_to_bytes(uint64_t filesize, uint64_t bsize); 172 | 173 | /* These are all the VIRTFS specific vops */ 174 | int virtfs_stat_vnode_l(void); 175 | int virtfs_stat_vnode_dotl(struct p9_stat_dotl *st, struct vnode *vp); 176 | int virtfs_reload_stats_dotl(struct vnode *vp); 177 | int virtfs_proto_dotl(struct virtfs_session *vses); 178 | struct p9_fid *virtfs_init_session(struct mount *mp, int *error); 179 | void virtfs_close_session(struct mount *mp); 180 | void virtfs_prepare_to_close(struct mount *mp); 181 | void virtfs_complete_close(struct mount *mp); 182 | int virtfs_vget(struct mount *mp, ino_t ino, int flags, struct vnode **vpp); 183 | int virtfs_vget_common(struct mount *mp, struct virtfs_node *np, int flags, 184 | struct virtfs_node *parent, struct p9_fid *fid, struct vnode **vpp, 185 | char *name); 186 | int virtfs_node_cmp(struct vnode *vp, void *arg); 187 | void virtfs_dispose_node(struct virtfs_node **npp); 188 | void virtfs_cleanup(struct virtfs_node *vp); 189 | void virtfs_fid_remove_all(struct virtfs_node *np); 190 | void virtfs_fid_remove(struct virtfs_node *np, struct p9_fid *vfid, 191 | int fid_type); 192 | void virtfs_fid_add(struct virtfs_node *np, struct p9_fid *fid, 193 | int fid_type); 194 | struct p9_fid *virtfs_get_fid_from_uid(struct virtfs_node *np, 195 | uid_t uid, int fid_type); 196 | struct p9_fid *virtfs_get_fid(struct p9_client *clnt, 197 | struct virtfs_node *np, int fid_type, int *error); 198 | #endif /* __VIRTFS__ */ 199 | -------------------------------------------------------------------------------- /sys/dev/virtio/9pfs/virtfs_proto.h: -------------------------------------------------------------------------------- 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 | * Plan9 filesystem (9P2000.u) protocol definitions. 28 | */ 29 | 30 | #ifndef __VIRTFS_PROTO_H__ 31 | #define __VIRTFS_PROTO_H__ 32 | 33 | #include 34 | 35 | /* QID: Unique identification for the file being accessed */ 36 | struct virtfs_qid { 37 | uint8_t qid_mode; /* file mode specifiying file type */ 38 | uint32_t qid_version; /* version of the file */ 39 | uint64_t qid_path; /* unique integer among all files in hierarchy */ 40 | }; 41 | 42 | /* File permissions */ 43 | #define VIRTFS_OREAD 0 44 | #define VIRTFS_OWRITE 1 45 | #define VIRTFS_ORDWR 2 46 | #define VIRTFS_OEXEC 3 47 | #define VIRTFS_OTRUNC 0x10 48 | 49 | #endif /* __VIRTFS_PROTO_H__ */ 50 | -------------------------------------------------------------------------------- /sys/dev/virtio/9pfs/virtfs_subr.c: -------------------------------------------------------------------------------- 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, VirtFS driver. This will interact with the client to make 30 | * sure we have correct API calls in the header. 31 | */ 32 | 33 | #include 34 | __FBSDID("$FreeBSD$"); 35 | 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | #include "virtfs_proto.h" 50 | #include 51 | #include 52 | #include 53 | #include "virtfs.h" 54 | 55 | int 56 | virtfs_proto_dotl(struct virtfs_session *vses) 57 | { 58 | 59 | return (vses->flags & VIRTFS_PROTO_2000L); 60 | } 61 | 62 | /* Initialize a VirtFS session */ 63 | struct p9_fid * 64 | virtfs_init_session(struct mount *mp, int *error) 65 | { 66 | struct virtfs_session *vses; 67 | struct virtfs_mount *virtmp; 68 | struct p9_fid *fid; 69 | char *access; 70 | 71 | virtmp = VFSTOP9(mp); 72 | vses = &virtmp->virtfs_session; 73 | vses->uid = P9_NONUNAME; 74 | vses->uname = P9_DEFUNAME; 75 | vses->aname = P9_DEFANAME; 76 | 77 | /* 78 | * Create the client structure. Call into the driver to create 79 | * driver structures for the actual IO transfer. 80 | */ 81 | vses->clnt = p9_client_create(mp, error, virtmp->mount_tag); 82 | 83 | if (vses->clnt == NULL) { 84 | p9_debug(ERROR, "problem initializing 9p client\n"); 85 | return NULL; 86 | } 87 | /* 88 | * Find the client version and cache the copy. We will use this copy 89 | * throughout FS layer. 90 | */ 91 | if (p9_is_proto_dotl(vses->clnt)) 92 | vses->flags |= VIRTFS_PROTO_2000L; 93 | else if (p9_is_proto_dotu(vses->clnt)) 94 | vses->flags |= VIRTFS_PROTO_2000U; 95 | 96 | /* Set the access mode */ 97 | access = vfs_getopts(mp->mnt_optnew, "access", error); 98 | if (access == NULL) 99 | vses->flags |= P9_ACCESS_USER; 100 | else if (!strcmp(access, "any")) 101 | vses->flags |= P9_ACCESS_ANY; 102 | else if (!strcmp(access, "single")) 103 | vses->flags |= P9_ACCESS_SINGLE; 104 | else if (!strcmp(access, "user")) 105 | vses->flags |= P9_ACCESS_USER; 106 | else { 107 | p9_debug(ERROR, "Unknown access mode\n"); 108 | *error = EINVAL; 109 | goto out; 110 | } 111 | 112 | *error = 0; 113 | /* Attach with the backend host*/ 114 | fid = p9_client_attach(vses->clnt, NULL, vses->uname, P9_NONUNAME, 115 | vses->aname, error); 116 | vses->mnt_fid = fid; 117 | 118 | if (*error != 0) { 119 | p9_debug(ERROR, "cannot attach\n"); 120 | goto out; 121 | } 122 | p9_debug(SUBR, "Attach successful fid :%p\n", fid); 123 | fid->uid = vses->uid; 124 | 125 | /* initialize the node list for the session */ 126 | STAILQ_INIT(&vses->virt_node_list); 127 | VIRTFS_LOCK_INIT(vses); 128 | 129 | p9_debug(SUBR, "INIT session successful\n"); 130 | 131 | return fid; 132 | out: 133 | p9_client_destroy(vses->clnt); 134 | return NULL; 135 | } 136 | 137 | /* Begin to terminate a session */ 138 | void 139 | virtfs_prepare_to_close(struct mount *mp) 140 | { 141 | struct virtfs_session *vses; 142 | struct virtfs_mount *vmp; 143 | 144 | vmp = VFSTOP9(mp); 145 | vses = &vmp->virtfs_session; 146 | 147 | /* We are about to teardown, we dont allow anything other than clunk after this.*/ 148 | p9_client_begin_disconnect(vses->clnt); 149 | } 150 | 151 | /* Shutdown a session */ 152 | void 153 | virtfs_complete_close(struct mount *mp) 154 | { 155 | struct virtfs_session *vses; 156 | struct virtfs_mount *vmp; 157 | 158 | vmp = VFSTOP9(mp); 159 | vses = &vmp->virtfs_session; 160 | 161 | /* Finish the close*/ 162 | p9_client_disconnect(vses->clnt); 163 | } 164 | 165 | 166 | /* Call from unmount. Close the session. */ 167 | void 168 | virtfs_close_session(struct mount *mp) 169 | { 170 | struct virtfs_session *vses; 171 | struct virtfs_mount *vmp; 172 | struct virtfs_node *p, *tmp; 173 | 174 | vmp = VFSTOP9(mp); 175 | vses = &vmp->virtfs_session; 176 | 177 | /* 178 | * Cleanup the leftover VirtFS nodes in this session. This could be all 179 | * removed, unlinked VirtFS nodes on the host. 180 | */ 181 | VIRTFS_LOCK(vses); 182 | STAILQ_FOREACH_SAFE(p, &vses->virt_node_list, virtfs_node_next, tmp) { 183 | 184 | virtfs_cleanup(p); 185 | } 186 | VIRTFS_UNLOCK(vses); 187 | virtfs_complete_close(mp); 188 | /* Clean up the clnt structure. */ 189 | p9_client_destroy(vses->clnt); 190 | VIRTFS_LOCK_DESTROY(vses); 191 | p9_debug(SUBR, " Clean close session .\n"); 192 | } 193 | 194 | /* 195 | * Remove all the fids of a particular type from a VirtFS node 196 | * as well as destroy/clunk them. 197 | */ 198 | void 199 | virtfs_fid_remove_all(struct virtfs_node *np) 200 | { 201 | struct p9_fid *fid, *tfid; 202 | 203 | STAILQ_FOREACH_SAFE(fid, &np->vfid_list, fid_next, tfid) { 204 | STAILQ_REMOVE(&np->vfid_list, fid, p9_fid, fid_next); 205 | p9_client_clunk(fid); 206 | } 207 | 208 | STAILQ_FOREACH_SAFE(fid, &np->vofid_list, fid_next, tfid) { 209 | STAILQ_REMOVE(&np->vofid_list, fid, p9_fid, fid_next); 210 | p9_client_clunk(fid); 211 | } 212 | } 213 | 214 | 215 | /* Remove a fid from its corresponding fid list */ 216 | void 217 | virtfs_fid_remove(struct virtfs_node *np, struct p9_fid *fid, int fid_type) 218 | { 219 | 220 | switch (fid_type) { 221 | case VFID: 222 | VIRTFS_VFID_LOCK(np); 223 | STAILQ_REMOVE(&np->vfid_list, fid, p9_fid, fid_next); 224 | VIRTFS_VFID_UNLOCK(np); 225 | break; 226 | case VOFID: 227 | VIRTFS_VOFID_LOCK(np); 228 | STAILQ_REMOVE(&np->vofid_list, fid, p9_fid, fid_next); 229 | VIRTFS_VOFID_UNLOCK(np); 230 | break; 231 | } 232 | } 233 | 234 | /* Add a fid to the corresponding fid list */ 235 | void 236 | virtfs_fid_add(struct virtfs_node *np, struct p9_fid *fid, int fid_type) 237 | { 238 | 239 | switch (fid_type) { 240 | case VFID: 241 | VIRTFS_VFID_LOCK(np); 242 | STAILQ_INSERT_TAIL(&np->vfid_list, fid, fid_next); 243 | VIRTFS_VFID_UNLOCK(np); 244 | break; 245 | case VOFID: 246 | VIRTFS_VOFID_LOCK(np); 247 | STAILQ_INSERT_TAIL(&np->vofid_list, fid, fid_next); 248 | VIRTFS_VOFID_UNLOCK(np); 249 | break; 250 | } 251 | } 252 | 253 | /* Build the path from root to current directory */ 254 | static int 255 | virtfs_get_full_path(struct virtfs_node *np, char ***names) 256 | { 257 | int i, n; 258 | struct virtfs_node *node; 259 | char **wnames; 260 | 261 | n = 0; 262 | for (node = np ; (node != NULL) && !IS_ROOT(node) ; node = node->parent) 263 | n++; 264 | 265 | if (node == NULL) 266 | return 0; 267 | 268 | wnames = malloc(n * sizeof(char *), M_TEMP, M_ZERO|M_WAITOK); 269 | 270 | for (i = n-1, node = np; i >= 0 ; i--, node = node->parent) 271 | wnames[i] = node->inode.i_name; 272 | 273 | *names = wnames; 274 | return n; 275 | } 276 | 277 | /* 278 | * Retrieve fid structure corresponding to a particular 279 | * uid and fid type for a VirtFS node 280 | */ 281 | struct p9_fid * 282 | virtfs_get_fid_from_uid(struct virtfs_node *np, uid_t uid, int fid_type) 283 | { 284 | struct p9_fid *fid; 285 | 286 | switch (fid_type) { 287 | case VFID: 288 | VIRTFS_VFID_LOCK(np); 289 | STAILQ_FOREACH(fid, &np->vfid_list, fid_next) { 290 | if (fid->uid == uid) { 291 | VIRTFS_VFID_UNLOCK(np); 292 | return fid; 293 | } 294 | } 295 | VIRTFS_VFID_UNLOCK(np); 296 | break; 297 | case VOFID: 298 | VIRTFS_VOFID_LOCK(np); 299 | STAILQ_FOREACH(fid, &np->vofid_list, fid_next) { 300 | if (fid->uid == uid) { 301 | VIRTFS_VOFID_UNLOCK(np); 302 | return fid; 303 | } 304 | } 305 | VIRTFS_VOFID_UNLOCK(np); 306 | break; 307 | } 308 | 309 | return NULL; 310 | } 311 | 312 | /* 313 | * Function returns the fid sturcture for a file corresponding to current user id. 314 | * First it searches in the fid list of the corresponding VirtFS node. 315 | * New fid will be created if not already present and added in the corresponding 316 | * fid list in the VirtFS node. 317 | * If the user is not already attached then this will attach the user first 318 | * and then create a new fid for this particular file by doing dir walk. 319 | */ 320 | struct p9_fid * 321 | virtfs_get_fid(struct p9_client *clnt, struct virtfs_node *np, int fid_type, 322 | int *error) 323 | { 324 | uid_t uid; 325 | struct thread *td; 326 | struct p9_fid *fid, *oldfid; 327 | struct virtfs_node *root; 328 | struct virtfs_session *vses; 329 | int i, l, clone; 330 | char **wnames = NULL; 331 | uint16_t nwnames; 332 | 333 | td = curthread; 334 | oldfid = NULL; 335 | vses = np->virtfs_ses; 336 | 337 | if (vses->flags & P9_ACCESS_ANY) 338 | uid = vses->uid; 339 | else 340 | uid = td->td_ucred->cr_uid; 341 | 342 | /* 343 | * Search for the fid in corresponding fid list. 344 | * We should return NULL for VOFID if it is not present in the list. 345 | * Because VOFID should have been created during the file open. 346 | * If VFID is not present in the list then we should create one. 347 | */ 348 | fid = virtfs_get_fid_from_uid(np, uid, fid_type); 349 | if (fid != NULL || fid_type == VOFID) 350 | return fid; 351 | 352 | /* Check root if the user is attached */ 353 | root = &np->virtfs_ses->rnp; 354 | fid = virtfs_get_fid_from_uid(root, uid, fid_type); 355 | if(fid == NULL) { 356 | /* Attach the user */ 357 | fid = p9_client_attach(clnt, NULL, NULL, uid, 358 | vses->aname, error); 359 | if (*error != 0) 360 | return NULL; 361 | virtfs_fid_add(root, fid, fid_type); 362 | } 363 | 364 | /* If we are looking for root then return it */ 365 | if (IS_ROOT(np)) 366 | return fid; 367 | 368 | /* If file is deleted, nothing to do */ 369 | if ((np->flags & VIRTFS_NODE_DELETED) != 0) { 370 | *error = ENOENT; 371 | return NULL; 372 | } 373 | 374 | /* Get full path from root to virtfs node */ 375 | nwnames = virtfs_get_full_path(np, &wnames); 376 | 377 | /* 378 | * Could not get full path. 379 | * If virtfs node is not deleted, parent should exist. 380 | */ 381 | KASSERT(nwnames != 0, ("%s: Directory of %s doesn't exist", __func__, np->inode.i_name)); 382 | 383 | clone = 1; 384 | i = 0; 385 | while (i < nwnames) { 386 | l = MIN(nwnames - i, P9_MAXWELEM); 387 | 388 | fid = p9_client_walk(fid, l, wnames, clone, error); 389 | if (*error != 0) { 390 | if (oldfid) 391 | p9_client_clunk(oldfid); 392 | fid = NULL; 393 | goto bail_out; 394 | } 395 | oldfid = fid; 396 | clone = 0; 397 | i += l ; 398 | } 399 | virtfs_fid_add(np, fid, fid_type); 400 | bail_out: 401 | free(wnames, M_TEMP); 402 | return fid; 403 | } 404 | -------------------------------------------------------------------------------- /sys/dev/virtio/9pfs/virtfs_vfops.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 2017-2020 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 | /* 28 | * This file consists of all the VFS interactions of VFS ops which include 29 | * mount, unmount, initilaize etc. for VirtFS. 30 | */ 31 | 32 | #include 33 | __FBSDID("$FreeBSD$"); 34 | 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include "virtfs_proto.h" 52 | #include 53 | #include 54 | #include "virtfs.h" 55 | 56 | /* This count is static now. Can be made tunable later */ 57 | #define VIRTFS_FLUSH_RETRIES 10 58 | 59 | static MALLOC_DEFINE(M_P9MNT, "virtfs_mount", "Mount structures for VirtFS"); 60 | static uma_zone_t virtfs_node_zone; 61 | uma_zone_t virtfs_io_buffer_zone; 62 | uma_zone_t virtfs_getattr_zone; 63 | uma_zone_t virtfs_setattr_zone; 64 | extern struct vop_vector virtfs_vnops; 65 | 66 | /* option parsing */ 67 | static const char *virtfs_opts[] = { 68 | "from", "trans", "access", NULL 69 | }; 70 | 71 | u_quad_t 72 | virtfs_round_filesize_to_bytes(uint64_t filesize, uint64_t bsize) 73 | { 74 | 75 | if (filesize == 0 && bsize == 0) 76 | return 0; 77 | return (((filesize + bsize - 1) / bsize) * bsize); 78 | } 79 | 80 | u_quad_t 81 | virtfs_pow2_filesize_to_bytes(uint64_t filesize, uint64_t bsize) 82 | { 83 | 84 | if (filesize == 0 || bsize == 0) 85 | return 0; 86 | return ((filesize + bsize - 1) & ~(bsize - 1)); 87 | } 88 | 89 | /* Dispose VirtFS node, freeing it to the UMA zone */ 90 | void 91 | virtfs_dispose_node(struct virtfs_node **npp) 92 | { 93 | struct virtfs_node *node; 94 | struct vnode *vp; 95 | 96 | node = *npp; 97 | 98 | if (node == NULL) 99 | return; 100 | 101 | p9_debug(VOPS, "dispose_node: %p\n", *npp); 102 | 103 | vp = VIRTFS_NTOV(node); 104 | vp->v_data = NULL; 105 | 106 | /* Free our associated memory */ 107 | if (!(vp->v_vflag & VV_ROOT)) { 108 | free(node->inode.i_name, M_TEMP); 109 | uma_zfree(virtfs_node_zone, node); 110 | } 111 | 112 | *npp = NULL; 113 | } 114 | 115 | /* Initialize memory allocation */ 116 | static int 117 | virtfs_init(struct vfsconf *vfsp) 118 | { 119 | 120 | virtfs_node_zone = uma_zcreate("VirtFS node zone", 121 | sizeof(struct virtfs_node), NULL, NULL, NULL, NULL, 0, 0); 122 | 123 | /* Create the getattr_dotl zone */ 124 | virtfs_getattr_zone = uma_zcreate("VirtFS getattr zone", 125 | sizeof(struct p9_stat_dotl), NULL, NULL, NULL, NULL, 0, 0); 126 | 127 | /* Create the setattr_dotl zone */ 128 | virtfs_setattr_zone = uma_zcreate("VirtFS setattr zone", 129 | sizeof(struct p9_iattr_dotl), NULL, NULL, NULL, NULL, 0, 0); 130 | 131 | /* 132 | * Create the io_buffer zone pool to keep things simpler in case of 133 | * multiple threads. Each thread works with its own so there is no 134 | * contention. 135 | */ 136 | virtfs_io_buffer_zone = uma_zcreate("VirtFS io_buffer zone", 137 | VIRTFS_MTU, NULL, NULL, NULL, NULL, 0, 0); 138 | 139 | return (0); 140 | } 141 | 142 | /* Destroy all the allocated memory */ 143 | static int 144 | virtfs_uninit(struct vfsconf *vfsp) 145 | { 146 | 147 | uma_zdestroy(virtfs_node_zone); 148 | uma_zdestroy(virtfs_io_buffer_zone); 149 | uma_zdestroy(virtfs_getattr_zone); 150 | uma_zdestroy(virtfs_setattr_zone); 151 | 152 | return (0); 153 | } 154 | 155 | /* Function to umount VirtFS */ 156 | static int 157 | virtfs_unmount(struct mount *mp, int mntflags) 158 | { 159 | struct virtfs_mount *vmp; 160 | struct virtfs_session *vses; 161 | int error, flags, i; 162 | 163 | error = 0; 164 | flags = 0; 165 | vmp = VFSTOP9(mp); 166 | if (vmp == NULL) 167 | return (0); 168 | 169 | vses = &vmp->virtfs_session; 170 | if (mntflags & MNT_FORCE) 171 | flags |= FORCECLOSE; 172 | 173 | virtfs_prepare_to_close(mp); 174 | for (i = 0; i < VIRTFS_FLUSH_RETRIES; i++) { 175 | 176 | /* Flush everything on this mount point.*/ 177 | error = vflush(mp, 1, flags, curthread); 178 | 179 | if (error == 0 || (mntflags & MNT_FORCE) == 0) 180 | break; 181 | /* Sleep until interrupted or 1 tick expires. */ 182 | error = tsleep(&error, PSOCK, "p9unmnt", 1); 183 | if (error == EINTR) 184 | break; 185 | error = EBUSY; 186 | } 187 | 188 | if (error != 0) 189 | goto out; 190 | virtfs_close_session(mp); 191 | /* Cleanup the mount structure. */ 192 | free(vmp, M_P9MNT); 193 | mp->mnt_data = NULL; 194 | return (error); 195 | out: 196 | /* Restore the flag in case of error */ 197 | vses->clnt->trans_status = VIRTFS_CONNECT; 198 | return (error); 199 | } 200 | 201 | /* 202 | * Compare qid stored in VirtFS node 203 | * Return 1 if does not match otherwise return 0 204 | */ 205 | int 206 | virtfs_node_cmp(struct vnode *vp, void *arg) 207 | { 208 | struct virtfs_node *np; 209 | struct p9_qid *qid; 210 | 211 | np = vp->v_data; 212 | qid = (struct p9_qid *)arg; 213 | 214 | if (np->vqid.qid_path == qid->path) { 215 | if (vp->v_vflag & VV_ROOT) 216 | return 0; 217 | else if (np->vqid.qid_mode == qid->type && 218 | np->vqid.qid_version == qid->version) 219 | return 0; 220 | } 221 | 222 | return 1; 223 | } 224 | 225 | /* 226 | * Common code used across VirtFS to return vnode for the file represented 227 | * by the fid. 228 | * Lookup for the vnode in hash_list. This lookup is based on the qid path 229 | * which is unique to a file. virtfs_node_cmp is called in this lookup process. 230 | * I. If the vnode we are looking for is found in the hash list 231 | * 1. Check if the vnode is a valid vnode by reloading its stats 232 | * a. if the reloading of the vnode stats returns error then remove the 233 | * vnode from hash list and return 234 | * b. If reloading of vnode stats returns without any error then, clunk the 235 | * new fid which was created for the vnode as we know that the vnode 236 | * already has a fid associated with it and return the vnode. 237 | * This is to avoid fid leaks 238 | * II. If vnode is not found in the hash list then, create new vnode, VirtFS 239 | * node and return the vnode 240 | */ 241 | int 242 | virtfs_vget_common(struct mount *mp, struct virtfs_node *np, int flags, 243 | struct virtfs_node *parent, struct p9_fid *fid, struct vnode **vpp, 244 | char *name) 245 | { 246 | struct virtfs_mount *vmp; 247 | struct virtfs_session *vses; 248 | struct vnode *vp; 249 | struct virtfs_node *node; 250 | struct thread *td; 251 | uint32_t hash; 252 | int error; 253 | struct virtfs_inode *inode; 254 | 255 | td = curthread; 256 | vmp = VFSTOP9(mp); 257 | vses = &vmp->virtfs_session; 258 | 259 | /* Look for vp in the hash_list */ 260 | hash = fnv_32_buf(&fid->qid.path, sizeof(uint64_t), FNV1_32_INIT); 261 | error = vfs_hash_get(mp, hash, flags, td, &vp, virtfs_node_cmp, 262 | &fid->qid); 263 | if (error != 0) 264 | return (error); 265 | else if (vp != NULL) { 266 | if (vp->v_vflag & VV_ROOT) { 267 | if (np == NULL) 268 | p9_client_clunk(fid); 269 | *vpp = vp; 270 | return (0); 271 | } 272 | error = virtfs_reload_stats_dotl(vp); 273 | if (error != 0) { 274 | node = vp->v_data; 275 | /* Remove stale vnode from hash list */ 276 | vfs_hash_remove(vp); 277 | node->flags |= VIRTFS_NODE_DELETED; 278 | 279 | vput(vp); 280 | *vpp = NULLVP; 281 | vp = NULL; 282 | } else { 283 | *vpp = vp; 284 | /* Clunk the new fid if not root */ 285 | p9_client_clunk(fid); 286 | return (0); 287 | } 288 | } 289 | 290 | /* 291 | * We must promote to an exclusive lock for vnode creation. This 292 | * can happen if lookup is passed LOCKSHARED. 293 | */ 294 | if ((flags & LK_TYPE_MASK) == LK_SHARED) { 295 | flags &= ~LK_TYPE_MASK; 296 | flags |= LK_EXCLUSIVE; 297 | } 298 | 299 | /* Allocate a new vnode. */ 300 | if ((error = getnewvnode("VirtFS", mp, &virtfs_vnops, &vp)) != 0) { 301 | *vpp = NULLVP; 302 | p9_debug(ERROR, "Couldnt allocate vnode from VFS \n"); 303 | return (error); 304 | } 305 | 306 | /* If we dont have it, create one. */ 307 | if (np == NULL) { 308 | np = uma_zalloc(virtfs_node_zone, M_WAITOK | M_ZERO); 309 | /* Initialize the VFID list */ 310 | VIRTFS_VFID_LOCK_INIT(np); 311 | STAILQ_INIT(&np->vfid_list); 312 | virtfs_fid_add(np, fid, VFID); 313 | 314 | /* Initialize the VOFID list */ 315 | VIRTFS_VOFID_LOCK_INIT(np); 316 | STAILQ_INIT(&np->vofid_list); 317 | 318 | np->parent = parent; 319 | np->virtfs_ses = vses; /* Map the current session */ 320 | inode = &np->inode; 321 | /*Fill the name of the file in inode */ 322 | inode->i_name = malloc(strlen(name)+1, M_TEMP, M_NOWAIT | M_ZERO); 323 | strlcpy(inode->i_name, name, strlen(name)+1); 324 | } else { 325 | vp->v_type = VDIR; /* root vp is a directory */ 326 | vp->v_vflag |= VV_ROOT; 327 | vref(vp); /* Increment a reference on root vnode during mount */ 328 | } 329 | 330 | vp->v_data = np; 331 | np->v_node = vp; 332 | inode = &np->inode; 333 | inode->i_qid_path = fid->qid.path; 334 | VIRTFS_SET_LINKS(inode); 335 | 336 | /* 337 | * Add the VirtFS node to the list for cleanup later. 338 | * Cleanup of this VirtFS node from the list of session 339 | * VirtFS nodes happen in vput() : 340 | * - In vfs_hash_insert() after inserting this node 341 | * to the VFS hash table. 342 | * - In error handling below. 343 | */ 344 | VIRTFS_LOCK(vses); 345 | STAILQ_INSERT_TAIL(&vses->virt_node_list, np, virtfs_node_next); 346 | VIRTFS_UNLOCK(vses); 347 | np->flags |= VIRTFS_NODE_IN_SESSION; 348 | 349 | lockmgr(vp->v_vnlock, LK_EXCLUSIVE, NULL); 350 | error = insmntque(vp, mp); 351 | if (error != 0) { 352 | /* 353 | * vput(vp) is already called from insmntque_stddtr(). 354 | * Just goto 'out' to dispose the node. 355 | */ 356 | goto out; 357 | } 358 | 359 | /* Init the vnode with the disk info*/ 360 | error = virtfs_reload_stats_dotl(vp); 361 | if (error != 0) { 362 | vput(vp); 363 | goto out; 364 | } 365 | 366 | error = vfs_hash_insert(vp, hash, flags, td, vpp, 367 | virtfs_node_cmp, &fid->qid); 368 | if (error != 0) { 369 | goto out; 370 | } 371 | if (*vpp == NULL) { 372 | *vpp = vp; 373 | } 374 | 375 | return (0); 376 | out: 377 | if (!IS_ROOT(np)) { 378 | /* Destroy the FID LIST locks */ 379 | VIRTFS_VFID_LOCK_DESTROY(np); 380 | VIRTFS_VOFID_LOCK_DESTROY(np); 381 | } 382 | 383 | /* Something went wrong, dispose the node */ 384 | 385 | /* 386 | * Remove the virtfs_node from the list before we cleanup. 387 | * This should ideally have been removed in vput() above. 388 | * We try again here, incase it is missed from vput(), as 389 | * we added this vnode explicitly to virt_node_list above. 390 | */ 391 | if ((np->flags & VIRTFS_NODE_IN_SESSION) != 0) { 392 | VIRTFS_LOCK(vses); 393 | STAILQ_REMOVE(&vses->virt_node_list, np, virtfs_node, virtfs_node_next); 394 | VIRTFS_UNLOCK(vses); 395 | np->flags &= ~VIRTFS_NODE_IN_SESSION; 396 | } 397 | virtfs_dispose_node(&np); 398 | *vpp = NULLVP; 399 | return (error); 400 | } 401 | 402 | /* Main mount function for 9pfs */ 403 | static int 404 | p9_mount(struct mount *mp) 405 | { 406 | struct p9_fid *fid; 407 | struct virtfs_mount *vmp; 408 | struct virtfs_session *vses; 409 | struct virtfs_node *virtfs_root; 410 | int error; 411 | char *from; 412 | int len; 413 | 414 | /* Verify the validity of mount options */ 415 | if (vfs_filteropt(mp->mnt_optnew, virtfs_opts)) 416 | return EINVAL; 417 | 418 | /* Extract NULL terminated mount tag from mount options */ 419 | error = vfs_getopt(mp->mnt_optnew, "from", (void **)&from, &len); 420 | if (error != 0 || from[len - 1] != '\0') 421 | return EINVAL; 422 | 423 | /* Allocate and initialize the private mount structure. */ 424 | vmp = malloc(sizeof (struct virtfs_mount), M_P9MNT, M_WAITOK | M_ZERO); 425 | mp->mnt_data = vmp; 426 | vmp->virtfs_mountp = mp; 427 | vmp->virtfs_filesize2bytes = virtfs_round_filesize_to_bytes; 428 | vmp->mount_tag = from; 429 | vmp->mount_tag_len = len; 430 | vses = &vmp->virtfs_session; 431 | vses->virtfs_mount = mp; 432 | virtfs_root = &vses->rnp; 433 | /* Hardware iosize from the Qemu */ 434 | mp->mnt_iosize_max = PAGE_SIZE; 435 | /* 436 | * Init the session for the VirtFS root. This creates a new root fid and 437 | * attaches the client and server. 438 | */ 439 | fid = virtfs_init_session(mp, &error); 440 | if (fid == NULL) { 441 | goto out; 442 | } 443 | 444 | VIRTFS_VFID_LOCK_INIT(virtfs_root); 445 | STAILQ_INIT(&virtfs_root->vfid_list); 446 | virtfs_fid_add(virtfs_root, fid, VFID); 447 | VIRTFS_VOFID_LOCK_INIT(virtfs_root); 448 | STAILQ_INIT(&virtfs_root->vofid_list); 449 | virtfs_root->parent = virtfs_root; 450 | virtfs_root->flags |= VIRTFS_ROOT; 451 | virtfs_root->virtfs_ses = vses; 452 | vfs_getnewfsid(mp); 453 | strlcpy(mp->mnt_stat.f_mntfromname, from, 454 | sizeof(mp->mnt_stat.f_mntfromname)); 455 | MNT_ILOCK(mp); 456 | mp->mnt_flag |= MNT_LOCAL; 457 | mp->mnt_kern_flag |= MNTK_LOOKUP_SHARED | MNTK_EXTENDED_SHARED; 458 | MNT_IUNLOCK(mp); 459 | p9_debug(VFS, "Mount successful\n"); 460 | /* Mount structures created. */ 461 | 462 | return (0); 463 | out: 464 | p9_debug(ERROR, " Mount Failed \n"); 465 | if (vmp != NULL) { 466 | free(vmp, M_P9MNT); 467 | mp->mnt_data = NULL; 468 | } 469 | return (error); 470 | } 471 | 472 | /* Mount entry point */ 473 | static int 474 | virtfs_mount(struct mount *mp) 475 | { 476 | int error; 477 | 478 | /* No support for UPDATE for now */ 479 | if (mp->mnt_flag & MNT_UPDATE) 480 | return (EOPNOTSUPP); 481 | 482 | error = p9_mount(mp); 483 | if (error != 0) 484 | (void) virtfs_unmount(mp, MNT_FORCE); 485 | 486 | return (error); 487 | } 488 | 489 | /* 490 | * Retrieve the root vnode of this mount. After filesystem is mounted, the root 491 | * vnode is created for the first time. Subsequent calls to VirtFS root will 492 | * return the same vnode created during mount. 493 | */ 494 | static int 495 | virtfs_root(struct mount *mp, int lkflags, struct vnode **vpp) 496 | { 497 | struct virtfs_mount *vmp; 498 | struct virtfs_node *np; 499 | struct p9_client *clnt; 500 | struct p9_fid *vfid; 501 | int error; 502 | 503 | vmp = VFSTOP9(mp); 504 | np = &vmp->virtfs_session.rnp; 505 | clnt = vmp->virtfs_session.clnt; 506 | error = 0; 507 | 508 | p9_debug(VOPS, "%s: node=%p name=%s\n",__func__, np, np->inode.i_name); 509 | 510 | vfid = virtfs_get_fid(clnt, np, VFID, &error); 511 | 512 | if (error != 0) { 513 | /* for root use the nobody user's fid as vfid. 514 | * This is used while unmounting as root when non-root 515 | * user has mounted VirtFS 516 | */ 517 | if (vfid == NULL && clnt->trans_status == VIRTFS_BEGIN_DISCONNECT) 518 | vfid = vmp->virtfs_session.mnt_fid; 519 | else { 520 | *vpp = NULLVP; 521 | return error; 522 | } 523 | } 524 | 525 | error = virtfs_vget_common(mp, np, lkflags, np, vfid, vpp, NULL); 526 | if (error != 0) { 527 | *vpp = NULLVP; 528 | return (error); 529 | } 530 | np->v_node = *vpp; 531 | return (error); 532 | } 533 | 534 | /* Retrieve the file system statistics */ 535 | static int 536 | virtfs_statfs(struct mount *mp __unused, struct statfs *buf) 537 | { 538 | struct virtfs_mount *vmp; 539 | struct virtfs_node *np; 540 | struct p9_client *clnt; 541 | struct p9_fid *vfid; 542 | struct p9_statfs statfs; 543 | int res, error; 544 | 545 | vmp = VFSTOP9(mp); 546 | np = &vmp->virtfs_session.rnp; 547 | clnt = vmp->virtfs_session.clnt; 548 | error = 0; 549 | 550 | vfid = virtfs_get_fid(clnt, np, VFID, &error); 551 | if (error != 0) { 552 | return error; 553 | } 554 | 555 | res = p9_client_statfs(vfid, &statfs); 556 | 557 | if (res == 0) { 558 | buf->f_type = statfs.type; 559 | /* 560 | * We have a limit of 4k irrespective of what the 561 | * Qemu server can do. 562 | */ 563 | if (statfs.bsize > PAGE_SIZE) 564 | buf->f_bsize = PAGE_SIZE; 565 | else 566 | buf->f_bsize = statfs.bsize; 567 | 568 | buf->f_iosize = buf->f_bsize; 569 | buf->f_blocks = statfs.blocks; 570 | buf->f_bfree = statfs.bfree; 571 | buf->f_bavail = statfs.bavail; 572 | buf->f_files = statfs.files; 573 | buf->f_ffree = statfs.ffree; 574 | } 575 | else { 576 | /* Atleast set these if stat fail */ 577 | buf->f_bsize = PAGE_SIZE; 578 | buf->f_iosize = buf->f_bsize; /* XXX */ 579 | } 580 | if ((buf->f_bsize & (buf->f_bsize -1)) == 0) 581 | vmp->virtfs_filesize2bytes = virtfs_pow2_filesize_to_bytes; 582 | 583 | return (0); 584 | } 585 | 586 | static int 587 | virtfs_fhtovp(struct mount *mp, struct fid *fhp, int flags, struct vnode **vpp) 588 | { 589 | 590 | return (EINVAL); 591 | } 592 | 593 | struct vfsops virtfs_vfsops = { 594 | .vfs_init = virtfs_init, 595 | .vfs_uninit = virtfs_uninit, 596 | .vfs_mount = virtfs_mount, 597 | .vfs_unmount = virtfs_unmount, 598 | .vfs_root = virtfs_root, 599 | .vfs_statfs = virtfs_statfs, 600 | .vfs_fhtovp = virtfs_fhtovp, 601 | }; 602 | VFS_SET(virtfs_vfsops, virtfs, VFCF_JAIL); 603 | MODULE_VERSION(vtfs, 1); 604 | MODULE_DEPEND(vtfs, virtio, 1, 1, 1); 605 | MODULE_DEPEND(vtfs, vt9p, 1, 1, 1); 606 | -------------------------------------------------------------------------------- /sys/dev/virtio/9pfs/virtfs_vnops.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2020 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 | /* This file contains VFS file ops for the 9P protocol. 28 | * This makes the upper layer of the VirtFS driver. These functions interact 29 | * with the VFS layer and lower layer of VirtFS driver which is 9Pnet. All 30 | * the user file operations are handled here. 31 | */ 32 | #include 33 | __FBSDID("$FreeBSD$"); 34 | 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | #include 53 | #include 54 | #include "virtfs_proto.h" 55 | #include "virtfs.h" 56 | #include 57 | 58 | /* File permissions. */ 59 | #define IEXEC 0000100 /* Executable. */ 60 | #define IWRITE 0000200 /* Writeable. */ 61 | #define IREAD 0000400 /* Readable. */ 62 | #define ISVTX 0001000 /* Sticky bit. */ 63 | #define ISGID 0002000 /* Set-gid. */ 64 | #define ISUID 0004000 /* Set-uid. */ 65 | 66 | static MALLOC_DEFINE(M_P9UIOV, "uio", "UIOV structures for strategy in VirtFS"); 67 | extern uma_zone_t virtfs_io_buffer_zone; 68 | extern uma_zone_t virtfs_getattr_zone; 69 | extern uma_zone_t virtfs_setattr_zone; 70 | /* For the root vnode's vnops. */ 71 | struct vop_vector virtfs_vnops; 72 | 73 | static uint32_t virtfs_unix2p9_mode(uint32_t mode); 74 | 75 | static void 76 | virtfs_itimes(struct vnode *vp) 77 | { 78 | struct virtfs_node *node; 79 | struct timespec ts; 80 | struct virtfs_inode *inode; 81 | 82 | node = VIRTFS_VTON(vp); 83 | inode = &node->inode; 84 | 85 | vfs_timestamp(&ts); 86 | inode->i_mtime = ts.tv_sec; 87 | } 88 | 89 | /* 90 | * Cleanup the VirtFS node, the in memory representation of a vnode for VirtFS. 91 | * The cleanup includes invalidating all cache entries for the vnode, 92 | * destroying the vobject, removing vnode from hashlist, removing VirtFS node 93 | * from the list of session VirtFS nodes, and disposing of the VirtFS node. 94 | * Basically it is doing a reverse of what a create/vget does. 95 | */ 96 | void 97 | virtfs_cleanup(struct virtfs_node *np) 98 | { 99 | struct vnode *vp; 100 | struct virtfs_session *vses; 101 | 102 | vp = VIRTFS_NTOV(np); 103 | vses = np->virtfs_ses; 104 | 105 | /* Invalidate all entries to a particular vnode. */ 106 | cache_purge(vp); 107 | /* Destroy the vm object and flush associated pages. */ 108 | vnode_destroy_vobject(vp); 109 | 110 | /* Remove the vnode from hash list if vnode is not already deleted */ 111 | if ((np->flags & VIRTFS_NODE_DELETED) == 0) 112 | vfs_hash_remove(vp); 113 | 114 | /* Remove all the FID */ 115 | virtfs_fid_remove_all(np); 116 | 117 | /* Destroy the FID LIST locks */ 118 | VIRTFS_VFID_LOCK_DESTROY(np); 119 | VIRTFS_VOFID_LOCK_DESTROY(np); 120 | 121 | /* Remove the virtfs_node from the list before we cleanup.*/ 122 | if ((np->flags & VIRTFS_NODE_IN_SESSION) != 0) { 123 | VIRTFS_LOCK(vses); 124 | STAILQ_REMOVE(&vses->virt_node_list, np, virtfs_node, virtfs_node_next); 125 | VIRTFS_UNLOCK(vses); 126 | np->flags &= ~VIRTFS_NODE_IN_SESSION; 127 | } 128 | 129 | /* Dispose all node knowledge.*/ 130 | virtfs_dispose_node(&np); 131 | } 132 | 133 | /* 134 | * Reclaim VOP is defined to be called for every vnode. This starts off 135 | * the cleanup by clunking(remove the fid on the server) and calls 136 | * virtfs_cleanup to free all the resources allocated for VirtFS node. 137 | */ 138 | static int 139 | virtfs_reclaim(struct vop_reclaim_args *ap) 140 | { 141 | struct vnode *vp; 142 | struct virtfs_node *np; 143 | 144 | vp = ap->a_vp; 145 | np = VIRTFS_VTON(vp); 146 | 147 | p9_debug(VOPS, "%s: vp:%p node:%p\n", __func__, vp, np); 148 | 149 | if (np == NULL) 150 | return (0); 151 | virtfs_cleanup(np); 152 | 153 | return (0); 154 | } 155 | /* 156 | * recycle vnodes which are no longer referenced i.e, their usecount is zero 157 | */ 158 | static int 159 | virtfs_inactive(struct vop_inactive_args *ap) 160 | { 161 | struct vnode *vp; 162 | struct virtfs_node *np; 163 | 164 | vp = ap->a_vp; 165 | np = VIRTFS_VTON(vp); 166 | 167 | p9_debug(VOPS, "%s: vp:%p node:%p file:%s\n", __func__, vp, np, np->inode.i_name); 168 | 169 | if(vp->v_usecount == 0) 170 | vrecycle(vp); 171 | 172 | return (0); 173 | } 174 | 175 | 176 | 177 | /* 178 | * virtfs_lookup is called for every component name that is being searched for. 179 | * 180 | * I. If component is found on the server, we look for the in-memory 181 | * repesentation(vnode) of this component in namecache. 182 | * A. If the node is found in the namecache, we check is the vnode is still 183 | * valid. 184 | * 1. If it is still valid, return vnode. 185 | * 2. If it is not valid, we remove this vnode from the name cache and 186 | * create a new vnode for the component and return that vnode. 187 | * B. If the vnode is not found in the namecache, we look for it in the 188 | * hash list. 189 | * 1. If the vnode is in the hash list, we check if the vnode is still 190 | * valid. 191 | * a. If it is still valid, we add that vnode to the namecache for 192 | * future lookups and return the vnode. 193 | * b. If it is not valid, create a new vnode and VirtFS node, 194 | * initialize them and return the vnode. 195 | * 2. If the vnode is not found in the hash list, we create a new vnode 196 | * and VirtFS node, initialize them and return the vnode. 197 | * II. If the component is not found on the server, an error code is returned. 198 | * A. For the creation case, we retutn EJUSTRETURN so VFS can handle it. 199 | * B. For all other cases, ENOENT is returned. 200 | */ 201 | static int 202 | virtfs_lookup(struct vop_lookup_args *ap) 203 | { 204 | struct vnode *dvp; 205 | struct vnode **vpp, *vp; 206 | struct componentname *cnp; 207 | struct virtfs_node *dnp; /*dir p9_node */ 208 | struct virtfs_node *np; 209 | struct virtfs_session *vses; 210 | struct mount *mp; /* Get the mount point */ 211 | struct p9_fid *dvfid, *newfid; 212 | int error, ltype; 213 | struct vattr vattr; 214 | int flags; 215 | char tmpchr; 216 | 217 | dvp = ap->a_dvp; 218 | vpp = ap->a_vpp; 219 | cnp = ap->a_cnp; 220 | dnp = VIRTFS_VTON(dvp); 221 | error = 0; 222 | flags = cnp->cn_flags; 223 | *vpp = NULLVP; 224 | 225 | if (dnp == NULL) 226 | return (ENOENT); 227 | 228 | vses = dnp->virtfs_ses; 229 | mp = vses->virtfs_mount; 230 | 231 | p9_debug(VOPS, "virtfs lookup\n"); 232 | /* Do the cache part ourselves */ 233 | if ((flags & ISLASTCN) && (mp->mnt_flag & MNT_RDONLY) && 234 | (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)) 235 | return (EROFS); 236 | 237 | if (dvp->v_type != VDIR) 238 | return (ENOTDIR); 239 | 240 | error = VOP_ACCESS(dvp, VEXEC, cnp->cn_cred, curthread); 241 | if (error) 242 | return (error); 243 | 244 | /* Do the directory walk on host to check if file exist */ 245 | dvfid = virtfs_get_fid(vses->clnt, dnp, VFID, &error); 246 | if (error) 247 | return error; 248 | 249 | /* 250 | * Save the character present at namelen in nameptr string and 251 | * null terminate the character to get the search name for p9_dir_walk 252 | * This is done to handle when lookup is for "a" and component 253 | * name contains a/b/c 254 | */ 255 | tmpchr = cnp->cn_nameptr[cnp->cn_namelen]; 256 | cnp->cn_nameptr[cnp->cn_namelen] = '\0'; 257 | 258 | /* 259 | * If the client_walk fails, it means the file looking for doesnt exist. 260 | * Create the file is the flags are set or just return the error 261 | */ 262 | if (cnp->cn_nameptr[0] == '.' && strlen(cnp->cn_nameptr) == 1) { 263 | newfid = p9_client_walk(dvfid, 0, NULL, 1, &error); 264 | } else { 265 | newfid = p9_client_walk(dvfid, 1, &cnp->cn_nameptr, 1, &error); 266 | } 267 | 268 | cnp->cn_nameptr[cnp->cn_namelen] = tmpchr; 269 | 270 | if (error != 0 || newfid == NULL) { 271 | /* Clunk the newfid if it is not NULL */ 272 | if (newfid != NULL) 273 | p9_client_clunk(newfid); 274 | 275 | if (error != ENOENT) 276 | return (error); 277 | 278 | /* The requested file was not found. */ 279 | if ((cnp->cn_nameiop == CREATE || cnp->cn_nameiop == RENAME) && 280 | (flags & ISLASTCN)) { 281 | 282 | if (mp->mnt_flag & MNT_RDONLY) 283 | return (EROFS); 284 | 285 | error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred, 286 | curthread); 287 | if (!error) { 288 | cnp->cn_flags |= SAVENAME; 289 | return (EJUSTRETURN); 290 | } 291 | } 292 | return (error); 293 | } 294 | 295 | /* Look for the entry in the component cache*/ 296 | error = cache_lookup(dvp, vpp, cnp, NULL, NULL); 297 | if (error > 0 && error != ENOENT) { 298 | p9_debug(VOPS, "Cache lookup error %d \n",error); 299 | goto out; 300 | } 301 | 302 | if (error == -1) { 303 | vp = *vpp; 304 | /* Check if the entry in cache is stale or not */ 305 | if ((virtfs_node_cmp(vp, &newfid->qid) == 0) && 306 | ((error = VOP_GETATTR(vp, &vattr, cnp->cn_cred)) == 0)) { 307 | if (cnp->cn_nameiop != LOOKUP && (flags & ISLASTCN)) 308 | cnp->cn_flags |= SAVENAME; 309 | goto out; 310 | } 311 | /* 312 | * This case, we have an error coming from getattr, 313 | * act accordingly. 314 | */ 315 | cache_purge(vp); 316 | if (dvp != vp) 317 | vput(vp); 318 | else 319 | vrele(vp); 320 | 321 | *vpp = NULLVP; 322 | } else if (error == ENOENT) { 323 | 324 | #if __FreeBSD_version >= 1300063 325 | if (dvp->v_iflag & VIRF_DOOMED) 326 | #else 327 | if (dvp->v_iflag & VI_DOOMED) 328 | #endif 329 | goto out; 330 | if (VOP_GETATTR(dvp, &vattr, cnp->cn_cred) == 0) { 331 | error = ENOENT; 332 | goto out; 333 | } 334 | cache_purge_negative(dvp); 335 | } 336 | /* Reset values */ 337 | error = 0; 338 | vp = NULLVP; 339 | 340 | tmpchr = cnp->cn_nameptr[cnp->cn_namelen]; 341 | cnp->cn_nameptr[cnp->cn_namelen] = '\0'; 342 | 343 | /* Looks like we have found an entry. Now take care of all other cases. */ 344 | if (flags & ISDOTDOT) { 345 | ltype = VOP_ISLOCKED(dvp); 346 | error = vfs_busy(mp, MBF_NOWAIT); 347 | 348 | if (error != 0) { 349 | vfs_ref(mp); 350 | #if __FreeBSD_version >= 1300074 351 | VOP_UNLOCK(dvp); 352 | #else 353 | VOP_UNLOCK(dvp, 0); 354 | #endif 355 | error = vfs_busy(mp, 0); 356 | VOP_LOCK(dvp, ltype | LK_RETRY); 357 | vfs_rel(mp); 358 | #if __FreeBSD_version >= 1300063 359 | if (error == 0 && (dvp->v_iflag & VIRF_DOOMED)) { 360 | #else 361 | if (error == 0 && (dvp->v_iflag & VI_DOOMED)) { 362 | #endif 363 | vfs_unbusy(mp); 364 | error = ENOENT; 365 | } 366 | if (error != 0) 367 | goto out; 368 | } 369 | #if __FreeBSD_version >= 1300074 370 | VOP_UNLOCK(dvp); 371 | #else 372 | VOP_UNLOCK(dvp, 0); 373 | #endif 374 | 375 | /* Try to create/reuse the node */ 376 | error = virtfs_vget_common(mp, NULL, cnp->cn_lkflags, dnp, newfid, &vp, 377 | cnp->cn_nameptr); 378 | if (error) 379 | goto out; 380 | p9_debug(VOPS, "Node created OK\n"); 381 | *vpp = vp; 382 | vfs_unbusy(mp); 383 | if (vp != dvp) 384 | VOP_LOCK(dvp, ltype | LK_RETRY); 385 | 386 | #if __FreeBSD_version >= 1300063 387 | if (dvp->v_iflag & VIRF_DOOMED) { 388 | #else 389 | if (dvp->v_iflag & VI_DOOMED) { 390 | #endif 391 | if (error == 0) { 392 | if (vp == dvp) 393 | vrele(vp); 394 | else 395 | vput(vp); 396 | } 397 | cnp->cn_nameptr[cnp->cn_namelen] = tmpchr; 398 | return (ENOENT); 399 | } 400 | } else { 401 | /* 402 | * client_walk is equivalent to searching a component name in a directory(fid) 403 | * here. If new fid is returned, we have found an entry for this component name 404 | * so, go and create the rest of the vnode infra(vget_common) for the returned 405 | * newfid. 406 | */ 407 | 408 | if ((cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME) 409 | && (flags & ISLASTCN)) { 410 | error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred, 411 | curthread); 412 | if (error) 413 | goto out; 414 | 415 | error = virtfs_vget_common(mp, NULL, cnp->cn_lkflags, dnp, newfid, &vp, 416 | cnp->cn_nameptr); 417 | if (error) 418 | goto out; 419 | 420 | *vpp = vp; 421 | np = VIRTFS_VTON(vp); 422 | if ((dnp->inode.i_mode & ISVTX) && 423 | cnp->cn_cred->cr_uid != 0 && 424 | cnp->cn_cred->cr_uid != dnp->inode.n_uid && 425 | cnp->cn_cred->cr_uid != np->inode.n_uid) { 426 | vput(*vpp); 427 | *vpp = NULL; 428 | cnp->cn_nameptr[cnp->cn_namelen] = tmpchr; 429 | return (EPERM); 430 | } 431 | } else { 432 | error = virtfs_vget_common(mp, NULL, cnp->cn_lkflags, dnp, newfid, &vp, 433 | cnp->cn_nameptr); 434 | if (error) 435 | goto out; 436 | *vpp = vp; 437 | } 438 | } 439 | 440 | cnp->cn_nameptr[cnp->cn_namelen] = tmpchr; 441 | 442 | if (cnp->cn_nameiop != LOOKUP && (flags & ISLASTCN)) 443 | cnp->cn_flags |= SAVENAME; 444 | 445 | /* Store the result the cache if MAKEENTRY is specified in flags */ 446 | if ((cnp->cn_flags & MAKEENTRY) != 0) 447 | cache_enter(dvp, *vpp, cnp); 448 | return (error); 449 | out: 450 | cnp->cn_nameptr[cnp->cn_namelen] = tmpchr; 451 | p9_client_clunk(newfid); 452 | return (error); 453 | } 454 | 455 | /* 456 | * Common creation function for file/directory with respective flags. We first 457 | * open the parent directory in order to create the file under it. For this, 458 | * as 9P protocol suggests, we need to call client_walk to create the open fid. 459 | * Once we have the open fid, the file_create function creates the direntry with 460 | * the name and perm specified under the parent dir. If this succeeds (an entry 461 | * is created for the new file on the server), we create our metadata for this 462 | * file (vnode, VirtFS node calling vget). Once we are done, we clunk the open 463 | * fid of the parent directory. 464 | */ 465 | static int 466 | create_common(struct virtfs_node *dnp, struct componentname *cnp, 467 | char *extension, uint32_t perm, uint8_t mode, struct vnode **vpp) 468 | { 469 | char tmpchr; 470 | struct p9_fid *dvfid, *ofid, *newfid; 471 | struct virtfs_session *vses; 472 | struct mount *mp; 473 | int error; 474 | 475 | p9_debug(VOPS, "name %s\n", cnp->cn_nameptr); 476 | 477 | vses = dnp->virtfs_ses; 478 | mp = vses->virtfs_mount; 479 | newfid = NULL; 480 | error = 0; 481 | 482 | dvfid = virtfs_get_fid(vses->clnt, dnp, VFID, &error); 483 | if (error != 0) 484 | return error; 485 | 486 | /* Clone the directory fid to create the new file */ 487 | ofid = p9_client_walk(dvfid, 0, NULL, 1, &error); 488 | if (error != 0) 489 | return (error); 490 | 491 | /* 492 | * Save the character present at namelen in nameptr string and 493 | * null terminate the character to get the search name for p9_dir_walk 494 | */ 495 | tmpchr = cnp->cn_nameptr[cnp->cn_namelen]; 496 | cnp->cn_nameptr[cnp->cn_namelen] = '\0'; 497 | 498 | error = p9_client_file_create(ofid, cnp->cn_nameptr, perm, mode, 499 | extension); 500 | if (error != 0) { 501 | p9_debug(ERROR, "p9_client_fcreate failed %d\n", error); 502 | goto out; 503 | } 504 | 505 | /* If its not hardlink only then do the walk, else we are done. */ 506 | if (!(perm & P9PROTO_DMLINK)) { 507 | /* 508 | * Do the lookup part and add the vnode, VirtFS node. Note that vpp 509 | * is filled in here. 510 | */ 511 | newfid = p9_client_walk(dvfid, 1, &cnp->cn_nameptr, 1, &error); 512 | if (newfid != NULL) { 513 | error = virtfs_vget_common(mp, NULL, cnp->cn_lkflags, 514 | dnp, newfid, vpp, cnp->cn_nameptr); 515 | if (error != 0) 516 | goto out; 517 | } else { 518 | /* Not found return NOENTRY.*/ 519 | goto out; 520 | } 521 | 522 | if ((cnp->cn_flags & MAKEENTRY) != 0) 523 | cache_enter(VIRTFS_NTOV(dnp), *vpp, cnp); 524 | } 525 | p9_debug(VOPS, "created file under vp %p node %p fid %ju\n", *vpp, dnp, 526 | (uintmax_t)dvfid->fid); 527 | /* Clunk the open ofid. */ 528 | if (ofid != NULL) 529 | (void)p9_client_clunk(ofid); 530 | 531 | cnp->cn_nameptr[cnp->cn_namelen] = tmpchr; 532 | return (0); 533 | out: 534 | if (ofid != NULL) 535 | (void)p9_client_clunk(ofid); 536 | 537 | if (newfid != NULL) 538 | (void)p9_client_clunk(newfid); 539 | 540 | cnp->cn_nameptr[cnp->cn_namelen] = tmpchr; 541 | return (error); 542 | } 543 | 544 | /* 545 | * This is the main file creation VOP. Make the permissions of the new 546 | * file and call the create_common common code to complete the create. 547 | */ 548 | static int 549 | virtfs_create(struct vop_create_args *ap) 550 | { 551 | struct vnode *dvp; 552 | struct vnode **vpp; 553 | struct componentname *cnp; 554 | uint32_t mode; 555 | struct virtfs_node *dnp; 556 | struct virtfs_inode *dinode; 557 | uint32_t perm; 558 | int ret; 559 | 560 | dvp = ap->a_dvp; 561 | vpp = ap->a_vpp; 562 | cnp = ap->a_cnp; 563 | dnp = VIRTFS_VTON(dvp); 564 | dinode = &dnp->inode; 565 | mode = MAKEIMODE(ap->a_vap->va_type, ap->a_vap->va_mode); 566 | perm = virtfs_unix2p9_mode(mode); 567 | 568 | p9_debug(VOPS, "%s: dvp %p\n", __func__, dvp); 569 | 570 | ret = create_common(dnp, cnp, NULL, perm, P9PROTO_ORDWR, vpp); 571 | if (ret == 0) { 572 | VIRTFS_INCR_LINKS(dinode); 573 | } 574 | 575 | return (ret); 576 | } 577 | 578 | /* 579 | * virtfs_mkdir is the main directory creation vop. Make the permissions of the new dir 580 | * and call the create_common common code to complete the create. 581 | */ 582 | static int 583 | virtfs_mkdir(struct vop_mkdir_args *ap) 584 | { 585 | struct vnode *dvp; 586 | struct vnode **vpp; 587 | struct componentname *cnp; 588 | uint32_t mode; 589 | struct virtfs_node *dnp; 590 | struct virtfs_inode *dinode; 591 | uint32_t perm; 592 | int ret; 593 | 594 | dvp = ap->a_dvp; 595 | vpp = ap->a_vpp; 596 | cnp = ap->a_cnp; 597 | dnp = VIRTFS_VTON(dvp); 598 | dinode = &dnp->inode; 599 | mode = MAKEIMODE(ap->a_vap->va_type, ap->a_vap->va_mode); 600 | perm = virtfs_unix2p9_mode(mode | S_IFDIR); 601 | 602 | p9_debug(VOPS, "%s: dvp %p\n", __func__, dvp); 603 | 604 | ret = create_common(dnp, cnp, NULL, perm, P9PROTO_ORDWR, vpp); 605 | if (ret == 0) 606 | VIRTFS_INCR_LINKS(dinode); 607 | 608 | return (ret); 609 | } 610 | 611 | /* 612 | * virtfs_mknod is the main node creation vop. Make the permissions of the new node 613 | * and call the create_common common code to complete the create. 614 | */ 615 | static int 616 | virtfs_mknod(struct vop_mknod_args *ap) 617 | { 618 | struct vnode *dvp; 619 | struct vnode **vpp; 620 | struct componentname *cnp; 621 | uint32_t mode; 622 | struct virtfs_node *dnp; 623 | struct virtfs_inode *dinode; 624 | uint32_t perm; 625 | int ret; 626 | 627 | dvp = ap->a_dvp; 628 | vpp = ap->a_vpp; 629 | cnp = ap->a_cnp; 630 | dnp = VIRTFS_VTON(dvp); 631 | dinode = &dnp->inode; 632 | mode = MAKEIMODE(ap->a_vap->va_type, ap->a_vap->va_mode); 633 | perm = virtfs_unix2p9_mode(mode); 634 | 635 | p9_debug(VOPS, "%s: dvp %p\n", __func__, dvp); 636 | 637 | ret = create_common(dnp, cnp, NULL, perm, P9PROTO_OREAD, vpp); 638 | if (ret == 0) { 639 | VIRTFS_INCR_LINKS(dinode); 640 | } 641 | 642 | return (ret); 643 | } 644 | 645 | /* Convert open mode permissions to P9 */ 646 | static int 647 | virtfs_uflags_mode(int uflags, int extended) 648 | { 649 | uint32_t ret; 650 | 651 | /* Convert first to O flags.*/ 652 | uflags = OFLAGS(uflags); 653 | 654 | switch (uflags & 3) { 655 | 656 | case O_RDONLY: 657 | ret = P9PROTO_OREAD; 658 | break; 659 | 660 | case O_WRONLY: 661 | ret = P9PROTO_OWRITE; 662 | break; 663 | 664 | case O_RDWR: 665 | ret = P9PROTO_ORDWR; 666 | break; 667 | } 668 | 669 | if (extended) { 670 | if (uflags & O_EXCL) 671 | ret |= P9PROTO_OEXCL; 672 | 673 | if (uflags & O_APPEND) 674 | ret |= P9PROTO_OAPPEND; 675 | } 676 | 677 | return (ret); 678 | } 679 | 680 | /* 681 | * This is the main open VOP for every file open. If the file is already 682 | * open, then increment and return. If there is no open fid for this file, 683 | * there needs to be a client_walk which creates a new open fid for this file. 684 | * Once we have a open fid, call the open on this file with the mode creating 685 | * the vobject. 686 | */ 687 | static int 688 | virtfs_open(struct vop_open_args *ap) 689 | { 690 | int error; 691 | struct vnode *vp; 692 | struct virtfs_node *np; 693 | struct virtfs_session *vses; 694 | struct p9_fid *vofid, *vfid; 695 | size_t filesize; 696 | uint32_t mode; 697 | 698 | error = 0; 699 | vp = ap->a_vp; 700 | np = VIRTFS_VTON(vp); 701 | vses = np->virtfs_ses; 702 | 703 | p9_debug(VOPS, "virtfs_open \n"); 704 | 705 | if (vp->v_type != VREG && vp->v_type != VDIR && vp->v_type != VLNK) 706 | return (EOPNOTSUPP); 707 | 708 | error = virtfs_reload_stats_dotl(vp); 709 | if (error != 0) 710 | return (error); 711 | 712 | ASSERT_VOP_LOCKED(vp, __func__); 713 | /* 714 | * Invalidate the pages of the vm_object cache if the file is modified 715 | * based on the flag set in reload stats 716 | */ 717 | if (vp->v_type == VREG && (np->flags & VIRTFS_NODE_MODIFIED) != 0) { 718 | error = vinvalbuf(vp, 0, 0, 0); 719 | if (error != 0) 720 | return error; 721 | np->flags &= ~VIRTFS_NODE_MODIFIED; 722 | } 723 | 724 | vfid = virtfs_get_fid(vses->clnt, np, VFID, &error); 725 | if (error != 0) 726 | return error; 727 | 728 | /* 729 | * Search the fid in vofid_list for current user. If found increase the open 730 | * count and return. If not found clone a new fid and open the file using 731 | * that cloned fid. 732 | */ 733 | vofid = virtfs_get_fid(vses->clnt, np, VOFID, &error); 734 | if (vofid != NULL) { 735 | vofid->v_opens++; 736 | return (0); 737 | } else { 738 | /*vofid is the open fid for this file.*/ 739 | vofid = p9_client_walk(vfid, 0, NULL, 1, &error); 740 | if (error != 0) 741 | return error; 742 | } 743 | 744 | /* 745 | * Always open file with RDWR permission to give permission 746 | * agnostic feeling for vp. Permission checking is done at 747 | * file descriptor level 748 | */ 749 | mode = virtfs_uflags_mode(ap->a_mode, 1); 750 | 751 | error = p9_client_open(vofid, mode); 752 | if (error != 0) 753 | p9_client_clunk(vofid); 754 | else { 755 | vofid->v_opens = 1; 756 | filesize = np->inode.i_size; 757 | vnode_create_vobject(vp, filesize, ap->a_td); 758 | virtfs_fid_add(np, vofid, VOFID); 759 | } 760 | 761 | return (error); 762 | } 763 | 764 | /* 765 | * Close the open references. Just reduce the open count on vofid and return. 766 | * Let clunking of VOFID happen in virtfs_reclaim. 767 | */ 768 | static int 769 | virtfs_close(struct vop_close_args *ap) 770 | { 771 | struct vnode *vp; 772 | struct virtfs_node *np; 773 | struct virtfs_session *vses; 774 | struct p9_fid *vofid; 775 | int error; 776 | 777 | vp = ap->a_vp; 778 | np = VIRTFS_VTON(vp); 779 | 780 | if (np == NULL) 781 | return (0); 782 | 783 | vses = np->virtfs_ses; 784 | error = 0; 785 | 786 | p9_debug(VOPS, "%s(file_name %s)\n", __func__, np->inode.i_name); 787 | 788 | vofid = virtfs_get_fid(vses->clnt, np, VOFID, &error); 789 | if (vofid == NULL) 790 | return (0); 791 | 792 | vofid->v_opens--; 793 | 794 | return (0); 795 | } 796 | 797 | /* Helper routine for checking if fileops are possible on this file */ 798 | static int 799 | virtfs_check_possible(struct vnode *vp, struct vattr *vap, mode_t mode) 800 | { 801 | 802 | /* Check if we are allowed to write */ 803 | switch (vap->va_type) { 804 | case VDIR: 805 | case VLNK: 806 | case VREG: 807 | /* 808 | * Normal nodes: check if we're on a read-only mounted 809 | * file system and bail out if we're trying to write. 810 | */ 811 | if ((mode & VMODIFY_PERMS) && (vp->v_mount->mnt_flag & MNT_RDONLY)) 812 | return (EROFS); 813 | break; 814 | case VBLK: 815 | case VCHR: 816 | case VSOCK: 817 | case VFIFO: 818 | /* 819 | * Special nodes: even on read-only mounted file systems 820 | * these are allowed to be written to if permissions allow. 821 | */ 822 | break; 823 | default: 824 | /* No idea what this is */ 825 | return (EINVAL); 826 | } 827 | 828 | return (0); 829 | } 830 | 831 | /* Check the access permissions of the file. */ 832 | static int 833 | virtfs_access(struct vop_access_args *ap) 834 | { 835 | struct vnode *vp; 836 | accmode_t accmode; 837 | struct ucred *cred; 838 | struct vattr vap; 839 | int error; 840 | 841 | vp = ap->a_vp; 842 | accmode = ap->a_accmode; 843 | cred = ap->a_cred; 844 | 845 | p9_debug(VOPS,"virtfs_access \n"); 846 | 847 | /* make sure getattr is working correctly and is defined.*/ 848 | error = VOP_GETATTR(vp, &vap, NULL); 849 | if (error != 0) 850 | return (error); 851 | 852 | error = virtfs_check_possible(vp, &vap, accmode); 853 | if (error != 0) 854 | return (error); 855 | 856 | /* Call the Generic Access check in VOPS*/ 857 | error = vaccess(vp->v_type, vap.va_mode, vap.va_uid, vap.va_gid, accmode, 858 | #if __FreeBSD_version >= 1300105 859 | cred); 860 | #else 861 | cred, NULL); 862 | #endif 863 | 864 | 865 | return (error); 866 | } 867 | 868 | /* 869 | * Reload the file stats from the server and update the inode structure present 870 | * in VirtFS node. 871 | */ 872 | int 873 | virtfs_reload_stats_dotl(struct vnode *vp) 874 | { 875 | struct p9_stat_dotl *stat; 876 | int error; 877 | struct virtfs_node *node; 878 | struct virtfs_session *vses; 879 | struct p9_fid *vfid; 880 | 881 | error = 0; 882 | node = VIRTFS_VTON(vp); 883 | vses = node->virtfs_ses; 884 | 885 | vfid = virtfs_get_fid(vses->clnt, node, VFID, &error); 886 | if (error) 887 | return error; 888 | 889 | stat = uma_zalloc(virtfs_getattr_zone, M_WAITOK | M_ZERO); 890 | 891 | error = p9_client_getattr(vfid, stat, P9PROTO_STATS_ALL); 892 | 893 | if (error != 0) { 894 | p9_debug(ERROR, "p9_client_getattr failed to reload stats\n"); 895 | goto out; 896 | } 897 | 898 | /* Init the vnode with the disk info */ 899 | virtfs_stat_vnode_dotl(stat, vp); 900 | out: 901 | if (stat != NULL) { 902 | uma_zfree(virtfs_getattr_zone, stat); 903 | } 904 | 905 | return (error); 906 | } 907 | 908 | /* 909 | * Read the current inode values into the vap attr. We reload the stats from 910 | * the server. 911 | */ 912 | static int 913 | virtfs_getattr_dotl(struct vop_getattr_args *ap) 914 | { 915 | struct vnode *vp; 916 | struct vattr *vap; 917 | struct virtfs_node *node; 918 | struct virtfs_inode *inode; 919 | int error; 920 | 921 | vp = ap->a_vp; 922 | vap = ap->a_vap; 923 | node = VIRTFS_VTON(vp); 924 | 925 | if (node == NULL) 926 | return (ENOENT); 927 | 928 | inode = &node->inode; 929 | 930 | p9_debug(VOPS, "getattr %u %u\n", inode->i_mode, IFTOVT(inode->i_mode)); 931 | 932 | /* Reload our stats once to get the right values.*/ 933 | error = virtfs_reload_stats_dotl(vp); 934 | if (error != 0) { 935 | p9_debug(ERROR, "virtfs_reload_stats_dotl failed %d\n", error); 936 | return (error); 937 | } 938 | 939 | /* Basic info */ 940 | VATTR_NULL(vap); 941 | 942 | vap->va_atime.tv_sec = inode->i_atime; 943 | vap->va_mtime.tv_sec = inode->i_mtime; 944 | vap->va_ctime.tv_sec = inode->i_ctime; 945 | vap->va_atime.tv_nsec = inode->i_atime_nsec; 946 | vap->va_mtime.tv_nsec = inode->i_mtime_nsec; 947 | vap->va_ctime.tv_nsec = inode->i_ctime_nsec; 948 | vap->va_type = IFTOVT(inode->i_mode); 949 | vap->va_mode = inode->i_mode; 950 | vap->va_uid = inode->n_uid; 951 | vap->va_gid = inode->n_gid; 952 | vap->va_fsid = vp->v_mount->mnt_stat.f_fsid.val[0]; 953 | vap->va_size = inode->i_size; 954 | vap->va_nlink = inode->i_links_count; 955 | vap->va_blocksize = inode->blksize; 956 | vap->va_fileid = inode->i_qid_path; 957 | vap->va_flags = inode->i_flags; 958 | vap->va_gen = inode->gen; 959 | vap->va_filerev = inode->data_version; 960 | vap->va_vaflags = 0; 961 | vap->va_bytes = inode->blocks * P9PROTO_TGETATTR_BLK; 962 | 963 | return (0); 964 | } 965 | 966 | /* Convert a standard FreeBSD permission to P9. */ 967 | static uint32_t 968 | virtfs_unix2p9_mode(uint32_t mode) 969 | { 970 | uint32_t res; 971 | 972 | res = mode & 0777; 973 | if (S_ISDIR(mode)) 974 | res |= P9PROTO_DMDIR; 975 | if (S_ISSOCK(mode)) 976 | res |= P9PROTO_DMSOCKET; 977 | if (S_ISLNK(mode)) 978 | res |= P9PROTO_DMSYMLINK; 979 | if (S_ISFIFO(mode)) 980 | res |= P9PROTO_DMNAMEDPIPE; 981 | if ((mode & S_ISUID) == S_ISUID) 982 | res |= P9PROTO_DMSETUID; 983 | if ((mode & S_ISGID) == S_ISGID) 984 | res |= P9PROTO_DMSETGID; 985 | if ((mode & S_ISVTX) == S_ISVTX) 986 | res |= P9PROTO_DMSETVTX; 987 | 988 | return (res); 989 | } 990 | 991 | /* Update inode with the stats read from server.(9P2000.L version) */ 992 | int 993 | virtfs_stat_vnode_dotl(struct p9_stat_dotl *stat, struct vnode *vp) 994 | { 995 | struct virtfs_node *np; 996 | struct virtfs_inode *inode; 997 | 998 | np = VIRTFS_VTON(vp); 999 | inode = &np->inode; 1000 | 1001 | ASSERT_VOP_LOCKED(vp, __func__); 1002 | /* Update the pager size if file size changes on host */ 1003 | if (inode->i_size != stat->st_size) { 1004 | inode->i_size = stat->st_size; 1005 | if (vp->v_type == VREG) 1006 | vnode_pager_setsize(vp, inode->i_size); 1007 | } 1008 | 1009 | inode->i_mtime = stat->st_mtime_sec; 1010 | inode->i_atime = stat->st_atime_sec; 1011 | inode->i_ctime = stat->st_ctime_sec; 1012 | inode->i_mtime_nsec = stat->st_mtime_nsec; 1013 | inode->i_atime_nsec = stat->st_atime_nsec; 1014 | inode->i_ctime_nsec = stat->st_ctime_nsec; 1015 | inode->n_uid = stat->st_uid; 1016 | inode->n_gid = stat->st_gid; 1017 | inode->i_mode = stat->st_mode; 1018 | vp->v_type = IFTOVT(inode->i_mode); 1019 | inode->i_links_count = stat->st_nlink; 1020 | inode->blksize = stat->st_blksize; 1021 | inode->blocks = stat->st_blocks; 1022 | inode->gen = stat->st_gen; 1023 | inode->data_version = stat->st_data_version; 1024 | 1025 | ASSERT_VOP_LOCKED(vp, __func__); 1026 | /* Setting a flag if file changes based on qid version */ 1027 | if (np->vqid.qid_version != stat->qid.version) 1028 | np->flags |= VIRTFS_NODE_MODIFIED; 1029 | memcpy(&np->vqid, &stat->qid, sizeof(stat->qid)); 1030 | 1031 | return (0); 1032 | } 1033 | 1034 | /* 1035 | * Write the current in memory inode stats into persistent stats structure 1036 | * to write to the server(for linux version). 1037 | */ 1038 | static int 1039 | virtfs_inode_to_iattr(struct virtfs_inode *inode, struct p9_iattr_dotl *p9attr) 1040 | { 1041 | p9attr->size = inode->i_size; 1042 | p9attr->mode = inode->i_mode; 1043 | p9attr->uid = inode->n_uid; 1044 | p9attr->gid = inode->n_gid; 1045 | p9attr->atime_sec = inode->i_atime; 1046 | p9attr->atime_nsec = inode->i_atime_nsec; 1047 | p9attr->mtime_sec = inode->i_mtime; 1048 | p9attr->mtime_nsec = inode->i_mtime_nsec; 1049 | 1050 | return (0); 1051 | } 1052 | 1053 | /* 1054 | * Modify the ownership of a file whenever the chown is called on the 1055 | * file. 1056 | */ 1057 | static int 1058 | virtfs_chown(struct vnode *vp, uid_t uid, gid_t gid, struct ucred *cred, 1059 | struct thread *td) 1060 | { 1061 | struct virtfs_node *np; 1062 | struct virtfs_inode *inode; 1063 | uid_t ouid; 1064 | gid_t ogid; 1065 | int error; 1066 | 1067 | np = VIRTFS_VTON(vp); 1068 | inode = &np->inode; 1069 | 1070 | if (uid == (uid_t)VNOVAL) 1071 | uid = inode->n_uid; 1072 | if (gid == (gid_t)VNOVAL) 1073 | gid = inode->n_gid; 1074 | /* 1075 | * To modify the ownership of a file, must possess VADMIN for that 1076 | * file. 1077 | */ 1078 | if ((error = VOP_ACCESSX(vp, VWRITE_OWNER, cred, td))) 1079 | return (error); 1080 | /* 1081 | * To change the owner of a file, or change the group of a file to a 1082 | * group of which we are not a member, the caller must have 1083 | * privilege. 1084 | */ 1085 | if (((uid != inode->n_uid && uid != cred->cr_uid) || 1086 | (gid != inode->n_gid && !groupmember(gid, cred))) && 1087 | #if __FreeBSD_version >= 1300005 1088 | (error = priv_check_cred(cred, PRIV_VFS_CHOWN))) 1089 | #else 1090 | (error = priv_check_cred(cred, PRIV_VFS_CHOWN, 0))) 1091 | #endif 1092 | return (error); 1093 | 1094 | ogid = inode->n_gid; 1095 | ouid = inode->n_uid; 1096 | 1097 | inode->n_gid = gid; 1098 | inode->n_uid = uid; 1099 | 1100 | if ((inode->i_mode & (ISUID | ISGID)) && 1101 | (ouid != uid || ogid != gid)) { 1102 | 1103 | #if __FreeBSD_version >= 1300005 1104 | if (priv_check_cred(cred, PRIV_VFS_RETAINSUGID)) 1105 | #else 1106 | if (priv_check_cred(cred, PRIV_VFS_RETAINSUGID, 0)) 1107 | #endif 1108 | inode->i_mode &= ~(ISUID | ISGID); 1109 | } 1110 | p9_debug(VOPS, "%s: vp %p, cred %p, td %p - ret OK\n", __func__, vp, cred, td); 1111 | 1112 | return (0); 1113 | } 1114 | 1115 | /* 1116 | * Update the in memory inode with all chmod new permissions/mode. Typically a 1117 | * setattr is called to update it to server. 1118 | */ 1119 | static int 1120 | virtfs_chmod(struct vnode *vp, uint32_t mode, struct ucred *cred, struct thread *td) 1121 | { 1122 | struct virtfs_node *np; 1123 | struct virtfs_inode *inode; 1124 | uint32_t nmode; 1125 | int error; 1126 | 1127 | np = VIRTFS_VTON(vp); 1128 | inode = &np->inode; 1129 | 1130 | p9_debug(VOPS, "%s: vp %p, mode %x, cred %p, td %p\n", __func__, vp, mode, cred, td); 1131 | /* 1132 | * To modify the permissions on a file, must possess VADMIN 1133 | * for that file. 1134 | */ 1135 | if ((error = VOP_ACCESS(vp, VADMIN, cred, td))) 1136 | return (error); 1137 | 1138 | /* 1139 | * Privileged processes may set the sticky bit on non-directories, 1140 | * as well as set the setgid bit on a file with a group that the 1141 | * process is not a member of. Both of these are allowed in 1142 | * jail(8). 1143 | */ 1144 | if (vp->v_type != VDIR && (mode & S_ISTXT)) { 1145 | #if __FreeBSD_version >= 1300005 1146 | if (priv_check_cred(cred, PRIV_VFS_STICKYFILE)) 1147 | #else 1148 | if (priv_check_cred(cred, PRIV_VFS_STICKYFILE, 0)) 1149 | #endif 1150 | return (EFTYPE); 1151 | } 1152 | if (!groupmember(inode->n_gid, cred) && (mode & ISGID)) { 1153 | #if __FreeBSD_version >= 1300005 1154 | error = priv_check_cred(cred, PRIV_VFS_SETGID); 1155 | #else 1156 | error = priv_check_cred(cred, PRIV_VFS_SETGID, 0); 1157 | #endif 1158 | if (error != 0) 1159 | return (error); 1160 | } 1161 | 1162 | /* 1163 | * Deny setting setuid if we are not the file owner. 1164 | */ 1165 | if ((mode & ISUID) && inode->n_uid != cred->cr_uid) { 1166 | #if __FreeBSD_version >= 1300005 1167 | error = priv_check_cred(cred, PRIV_VFS_ADMIN); 1168 | #else 1169 | error = priv_check_cred(cred, PRIV_VFS_ADMIN, 0); 1170 | #endif 1171 | if (error != 0) 1172 | return (error); 1173 | } 1174 | nmode = inode->i_mode; 1175 | nmode &= ~ALLPERMS; 1176 | nmode |= (mode & ALLPERMS); 1177 | inode->i_mode = nmode; 1178 | 1179 | p9_debug(VOPS, "%s: to mode %x %d \n ", __func__, nmode, error); 1180 | 1181 | return (error); 1182 | } 1183 | 1184 | /* 1185 | * Set the attributes of a file referenced by fid. A valid bitmask is sent 1186 | * in request selecting which fields to set 1187 | */ 1188 | static int 1189 | virtfs_setattr_dotl(struct vop_setattr_args *ap) 1190 | { 1191 | struct vnode *vp; 1192 | struct vattr *vap; 1193 | struct virtfs_node *node; 1194 | struct virtfs_inode *inode; 1195 | struct ucred *cred; 1196 | struct thread *td; 1197 | struct p9_iattr_dotl *p9attr; 1198 | struct virtfs_session *vses; 1199 | struct p9_fid *vfid; 1200 | uint64_t oldfilesize; 1201 | int error; 1202 | 1203 | vp = ap->a_vp; 1204 | vap = ap->a_vap; 1205 | node = VIRTFS_VTON(vp); 1206 | inode = &node->inode; 1207 | cred = ap->a_cred; 1208 | td = curthread; 1209 | vses = node->virtfs_ses; 1210 | error = 0; 1211 | 1212 | if ((vap->va_type != VNON) || (vap->va_nlink != VNOVAL) || 1213 | (vap->va_fsid != VNOVAL) || (vap->va_fileid != VNOVAL) || 1214 | (vap->va_blocksize != VNOVAL) || (vap->va_rdev != VNOVAL) || 1215 | (vap->va_bytes != VNOVAL) || (vap->va_gen != VNOVAL)) { 1216 | p9_debug(ERROR, "%s: unsettable attribute\n", __func__); 1217 | return (EINVAL); 1218 | } 1219 | /* Disallow write attemps on read only filesystem */ 1220 | if (vp->v_mount->mnt_flag & MNT_RDONLY) 1221 | return (EROFS); 1222 | 1223 | /* Setting of flags is not supported */ 1224 | if (vap->va_flags != VNOVAL) 1225 | return (EOPNOTSUPP); 1226 | 1227 | /* Allocate p9attr struct */ 1228 | p9attr = uma_zalloc(virtfs_setattr_zone, M_WAITOK | M_ZERO); 1229 | if (p9attr == NULL) 1230 | return (ENOMEM); 1231 | 1232 | /* Check if we need to change the ownership of the file*/ 1233 | if (vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL) { 1234 | p9_debug(VOPS, "%s: vp:%p td:%p uid/gid %x/%x\n", __func__, 1235 | vp, td, vap->va_uid, vap->va_gid); 1236 | 1237 | error = virtfs_chown(vp, vap->va_uid, vap->va_gid, cred, td); 1238 | p9attr->valid |= P9PROTO_SETATTR_UID | P9PROTO_SETATTR_GID | 1239 | P9PROTO_SETATTR_MODE; 1240 | if (error) 1241 | goto out; 1242 | } 1243 | 1244 | /* Check for mode changes */ 1245 | if (vap->va_mode != (mode_t)VNOVAL) { 1246 | p9_debug(VOPS, "%s: vp:%p td:%p mode %x\n", __func__, vp, td, 1247 | vap->va_mode); 1248 | 1249 | error = virtfs_chmod(vp, (int)vap->va_mode, cred, td); 1250 | p9attr->valid |= P9PROTO_SETATTR_MODE; 1251 | if (error) 1252 | goto out; 1253 | } 1254 | 1255 | /* Update the size of the file and update mtime */ 1256 | if (vap->va_size != (uint64_t)VNOVAL) { 1257 | p9_debug(VOPS, "%s: vp:%p td:%p size:%jx\n", __func__, 1258 | vp, td, (uintmax_t)vap->va_size); 1259 | switch (vp->v_type) { 1260 | case VDIR: 1261 | error = EISDIR; 1262 | goto out; 1263 | case VLNK: 1264 | case VREG: 1265 | /* Invalidate cached pages of vp */ 1266 | error = vinvalbuf(vp, 0, 0, 0); 1267 | if (error) 1268 | goto out; 1269 | oldfilesize = inode->i_size; 1270 | inode->i_size = vap->va_size; 1271 | /* Update the virtfs_inode time */ 1272 | virtfs_itimes(vp); 1273 | p9attr->valid |= P9PROTO_SETATTR_SIZE | 1274 | P9PROTO_SETATTR_ATIME | 1275 | P9PROTO_SETATTR_MTIME | 1276 | P9PROTO_SETATTR_ATIME_SET | 1277 | P9PROTO_SETATTR_MTIME_SET ; 1278 | break; 1279 | default: 1280 | goto out; 1281 | } 1282 | } else if (vap->va_atime.tv_sec != VNOVAL || 1283 | vap->va_mtime.tv_sec != VNOVAL) { 1284 | p9_debug(VOPS, "%s: vp:%p td:%p time a/m %jx/%jx/\n", 1285 | __func__, vp, td, (uintmax_t)vap->va_atime.tv_sec, 1286 | (uintmax_t)vap->va_mtime.tv_sec); 1287 | /* Update the virtfs_inode times */ 1288 | virtfs_itimes(vp); 1289 | p9attr->valid |= P9PROTO_SETATTR_ATIME | 1290 | P9PROTO_SETATTR_MTIME | P9PROTO_SETATTR_ATIME_SET | 1291 | P9PROTO_SETATTR_MTIME_SET; 1292 | } 1293 | 1294 | vfid = virtfs_get_fid(vses->clnt, node, VFID, &error); 1295 | if (error) 1296 | goto out; 1297 | /* Write the inode structure values into p9attr */ 1298 | virtfs_inode_to_iattr(inode, p9attr); 1299 | error = p9_client_setattr(vfid, p9attr); 1300 | if (vap->va_size != (uint64_t)VNOVAL && vp->v_type == VREG) { 1301 | if (error) 1302 | inode->i_size = oldfilesize; 1303 | else 1304 | vnode_pager_setsize(vp, inode->i_size); 1305 | } 1306 | out: 1307 | if (p9attr) { 1308 | uma_zfree(virtfs_setattr_zone, p9attr); 1309 | } 1310 | p9_debug(VOPS, "error code for p9_client_setattr %d \n",error); 1311 | return (error); 1312 | } 1313 | 1314 | /* 1315 | * An I/O buffer is used to to do any transfer. The uio is the vfs structure we 1316 | * need to copy data into. As long as resid is greater than zero, we call 1317 | * client_read to read data from offset(offset into the file) in the open fid 1318 | * for the file into the I/O buffer. The data is read into the user data buffer. 1319 | */ 1320 | static int 1321 | virtfs_read(struct vop_read_args *ap) 1322 | { 1323 | struct vnode *vp; 1324 | struct uio *uio; 1325 | struct virtfs_node *np; 1326 | struct virtfs_session *vses; 1327 | uint64_t offset; 1328 | int64_t ret; 1329 | uint64_t resid; 1330 | uint32_t count; 1331 | int error; 1332 | char *io_buffer; 1333 | uint64_t filesize; 1334 | struct p9_fid *vofid; 1335 | 1336 | vp = ap->a_vp; 1337 | uio = ap->a_uio; 1338 | np = VIRTFS_VTON(vp); 1339 | vses = np->virtfs_ses; 1340 | error = 0; 1341 | 1342 | if (vp->v_type == VCHR || vp->v_type == VBLK) 1343 | return (EOPNOTSUPP); 1344 | 1345 | if (vp->v_type != VREG) 1346 | return (EISDIR); 1347 | 1348 | if (uio->uio_resid == 0) 1349 | return (0); 1350 | 1351 | if (uio->uio_offset < 0) 1352 | return (EINVAL); 1353 | 1354 | vofid = virtfs_get_fid(vses->clnt, np, VOFID, &error); 1355 | if (vofid == NULL) { 1356 | p9_debug(ERROR, "Reading with NULL FID\n"); 1357 | return EBADF; 1358 | } 1359 | 1360 | /* where in the file are we to start reading */ 1361 | offset = uio->uio_offset; 1362 | filesize = np->inode.i_size; 1363 | if(uio->uio_offset >= filesize) 1364 | return (0); 1365 | 1366 | p9_debug(VOPS, "virtfs_read called %jd at %ju\n", 1367 | (intmax_t)uio->uio_resid, (uintmax_t)uio->uio_offset); 1368 | 1369 | /* Work with a local buffer from the pool for this vop */ 1370 | 1371 | io_buffer = uma_zalloc(virtfs_io_buffer_zone, M_WAITOK | M_ZERO); 1372 | while ((resid = uio->uio_resid) > 0) { 1373 | 1374 | if (offset >= filesize) 1375 | break; 1376 | 1377 | count = MIN(filesize - uio->uio_offset , resid); 1378 | 1379 | if (count == 0) 1380 | break; 1381 | 1382 | /* Copy count bytes into the uio */ 1383 | ret = p9_client_read(vofid, offset, count, io_buffer); 1384 | /* 1385 | * This is the only place in the entire VirtFS where we check the error 1386 | * for < 0 as p9_client_read/write return the number of bytes instead of 1387 | * an error code. In this case if ret is < 0, it means there is an IO error. 1388 | */ 1389 | if (ret < 0) 1390 | goto out; 1391 | 1392 | error = uiomove(io_buffer, ret, uio); 1393 | 1394 | if (error != 0) 1395 | goto out; 1396 | 1397 | offset += ret; 1398 | } 1399 | uio->uio_offset = offset; 1400 | out: 1401 | if (ret < 0) 1402 | error = -ret; 1403 | 1404 | uma_zfree(virtfs_io_buffer_zone, io_buffer); 1405 | 1406 | return (error); 1407 | } 1408 | 1409 | /* 1410 | * The user buffer contains the data to be written. This data is copied first 1411 | * from uio into I/O buffer. This I/O buffer is used to do the client_write to 1412 | * the fid of the file starting from the offset given upto count bytes. The 1413 | * number of bytes written is returned to the caller. 1414 | */ 1415 | static int 1416 | virtfs_write(struct vop_write_args *ap) 1417 | { 1418 | struct vnode *vp; 1419 | struct uio *uio; 1420 | struct virtfs_node *np; 1421 | struct virtfs_session *vses; 1422 | uint64_t off, offset; 1423 | int64_t ret; 1424 | uint64_t resid; 1425 | uint32_t count; 1426 | int error, ioflag; 1427 | uint64_t file_size; 1428 | char *io_buffer; 1429 | struct p9_fid *vofid; 1430 | 1431 | vp = ap->a_vp; 1432 | uio = ap->a_uio; 1433 | np = VIRTFS_VTON(vp); 1434 | vses = np->virtfs_ses; 1435 | error = 0; 1436 | ioflag = ap->a_ioflag; 1437 | 1438 | vofid = virtfs_get_fid(vses->clnt, np, VOFID, &error); 1439 | if (vofid == NULL) { 1440 | p9_debug(ERROR, "Writing with NULL FID\n"); 1441 | return EBADF; 1442 | } 1443 | p9_debug(VOPS, "virtfs_write called %#zx at %#jx\n", 1444 | uio->uio_resid, (uintmax_t)uio->uio_offset); 1445 | 1446 | if (uio->uio_offset < 0) 1447 | return (EINVAL); 1448 | if (uio->uio_resid == 0) 1449 | return (0); 1450 | 1451 | file_size = np->inode.i_size; 1452 | 1453 | switch (vp->v_type) { 1454 | case VREG: 1455 | if (ioflag & IO_APPEND) 1456 | uio->uio_offset = file_size; 1457 | break; 1458 | case VDIR: 1459 | return (EISDIR); 1460 | case VLNK: 1461 | break; 1462 | default: 1463 | panic("%s: bad file type vp: %p", __func__, vp); 1464 | } 1465 | 1466 | resid = uio->uio_resid; 1467 | offset = uio->uio_offset; 1468 | error = 0; 1469 | 1470 | io_buffer = uma_zalloc(virtfs_io_buffer_zone, M_WAITOK | M_ZERO); 1471 | while ((resid = uio->uio_resid) > 0) { 1472 | 1473 | off = 0; 1474 | count = MIN(resid, VIRTFS_IOUNIT); 1475 | error = uiomove(io_buffer, count, uio); 1476 | 1477 | if (error != 0) { 1478 | p9_debug(ERROR, "uiomove error in virtfs_write\n"); 1479 | goto out; 1480 | } 1481 | 1482 | /* While count still exists, keep writing.*/ 1483 | while (count > 0) { 1484 | /* Copy count bytes from the uio */ 1485 | ret = p9_client_write(vofid, offset, count, 1486 | io_buffer + off); 1487 | if (ret < 0) 1488 | goto out; 1489 | p9_debug(VOPS, "virtfs_write called %#zx at %#jx\n", 1490 | uio->uio_resid, (uintmax_t)uio->uio_offset); 1491 | 1492 | off += ret; 1493 | offset += ret; 1494 | count -= ret; 1495 | } 1496 | } 1497 | /* Update the fields in the node to reflect the change*/ 1498 | if (file_size < uio->uio_offset + uio->uio_resid) { 1499 | np->inode.i_size = uio->uio_offset + uio->uio_resid; 1500 | vnode_pager_setsize(vp, uio->uio_offset + uio->uio_resid); 1501 | } 1502 | out: 1503 | if (ret < 0) 1504 | error = -ret; 1505 | 1506 | uma_zfree(virtfs_io_buffer_zone, io_buffer); 1507 | 1508 | return (error); 1509 | } 1510 | 1511 | /* 1512 | * Common handler of all removal-related VOPs (e.g. rmdir, rm). Perform the 1513 | * client_remove op to send messages to remove the node's fid on the server. 1514 | * After that, does a node metadata cleanup on client side. 1515 | */ 1516 | static int 1517 | remove_common(struct virtfs_node *np) 1518 | { 1519 | int error; 1520 | struct virtfs_session *vses; 1521 | struct vnode *vp; 1522 | struct p9_fid *vfid; 1523 | 1524 | error = 0; 1525 | vses = np->virtfs_ses; 1526 | vp = VIRTFS_NTOV(np); 1527 | 1528 | vfid = virtfs_get_fid(vses->clnt, np, VFID, &error); 1529 | if (error != 0) 1530 | return error; 1531 | 1532 | error = p9_client_remove(vfid); 1533 | if (error != 0) 1534 | return (error); 1535 | 1536 | /* Remove all fids associated with the vp */ 1537 | virtfs_fid_remove_all(np); 1538 | 1539 | /* Invalidate all entries of vnode from name cache and hash list. */ 1540 | cache_purge(vp); 1541 | 1542 | vfs_hash_remove(vp); 1543 | np->flags |= VIRTFS_NODE_DELETED; 1544 | 1545 | return (error); 1546 | } 1547 | 1548 | /* Remove vop for all files. Call common code for remove and adjust links */ 1549 | static int 1550 | virtfs_remove(struct vop_remove_args *ap) 1551 | { 1552 | struct vnode *vp; 1553 | struct virtfs_node *np; 1554 | struct vnode *dvp; 1555 | struct virtfs_node *dnp; 1556 | struct virtfs_inode *dinode; 1557 | int error; 1558 | 1559 | vp = ap->a_vp; 1560 | np = VIRTFS_VTON(vp); 1561 | dvp = ap->a_dvp; 1562 | dnp = VIRTFS_VTON(dvp); 1563 | dinode = &dnp->inode; 1564 | 1565 | p9_debug(VOPS, "%s: vp %p node %p \n", __func__, vp, np); 1566 | 1567 | if (vp->v_type == VDIR) 1568 | return (EISDIR); 1569 | 1570 | error = remove_common(np); 1571 | if (error == 0) 1572 | VIRTFS_DECR_LINKS(dinode); 1573 | 1574 | return (error); 1575 | } 1576 | 1577 | /* Remove vop for all directories. Call common code for remove and adjust links */ 1578 | static int 1579 | virtfs_rmdir(struct vop_rmdir_args *ap) 1580 | { 1581 | struct vnode *vp; 1582 | struct virtfs_node *np; 1583 | struct vnode *dvp; 1584 | struct virtfs_node *dnp; 1585 | struct virtfs_inode *dinode; 1586 | int error; 1587 | 1588 | vp = ap->a_vp; 1589 | np = VIRTFS_VTON(vp); 1590 | dvp = ap->a_dvp; 1591 | dnp = VIRTFS_VTON(dvp); 1592 | dinode = &dnp->inode; 1593 | 1594 | p9_debug(VOPS, "%s: vp %p node %p \n", __func__, vp, np); 1595 | 1596 | error = remove_common(np); 1597 | if (error == 0) 1598 | VIRTFS_DECR_LINKS(dinode); 1599 | 1600 | return (error); 1601 | } 1602 | 1603 | /* 1604 | * Create symlinks. Make the permissions and call create_common code 1605 | * for Soft links. 1606 | */ 1607 | static int 1608 | virtfs_symlink(struct vop_symlink_args *ap) 1609 | { 1610 | struct vnode *dvp; 1611 | struct vnode **vpp; 1612 | struct vattr *vap; 1613 | struct componentname *cnp; 1614 | char *symtgt; 1615 | struct virtfs_node *dnp; 1616 | struct virtfs_session *vses; 1617 | struct mount *mp; 1618 | struct p9_fid *dvfid, *newfid; 1619 | int error; 1620 | char tmpchr; 1621 | gid_t gid; 1622 | 1623 | dvp = ap->a_dvp; 1624 | vpp = ap->a_vpp; 1625 | vap = ap->a_vap; 1626 | cnp = ap->a_cnp; 1627 | symtgt = ap->a_target; 1628 | dnp = VIRTFS_VTON(dvp); 1629 | vses = dnp->virtfs_ses; 1630 | mp = vses->virtfs_mount; 1631 | newfid = NULL; 1632 | error = 0; 1633 | gid = vap->va_gid; 1634 | 1635 | p9_debug(VOPS,"virtfs_symlink\n"); 1636 | p9_debug(VOPS, "%s: dvp %p\n", __func__, dvp); 1637 | 1638 | /* 1639 | * Save the character present at namelen in nameptr string and 1640 | * null terminate the character to get the search name for p9_dir_walk 1641 | */ 1642 | tmpchr = cnp->cn_nameptr[cnp->cn_namelen]; 1643 | cnp->cn_nameptr[cnp->cn_namelen] = '\0'; 1644 | 1645 | dvfid = virtfs_get_fid(vses->clnt, dnp, VFID, &error); 1646 | if (error != 0) 1647 | goto out; 1648 | 1649 | error = p9_create_symlink(dvfid, cnp->cn_nameptr, symtgt, gid); 1650 | if (error != 0) 1651 | goto out; 1652 | 1653 | /*create vnode for symtgt */ 1654 | newfid = p9_client_walk(dvfid, 1, &cnp->cn_nameptr, 1, &error); 1655 | if (newfid != NULL) { 1656 | error = virtfs_vget_common(mp, NULL, cnp->cn_lkflags, 1657 | dnp, newfid, vpp, cnp->cn_nameptr); 1658 | if (error != 0) 1659 | goto out; 1660 | } else 1661 | goto out; 1662 | 1663 | if ((cnp->cn_flags & MAKEENTRY) != 0) { 1664 | cache_enter(VIRTFS_NTOV(dnp), *vpp, cnp); 1665 | } 1666 | p9_debug(VOPS, "created file under vp %p node %p fid %ju\n", *vpp, 1667 | dnp, (uintmax_t)dvfid->fid); 1668 | 1669 | cnp->cn_nameptr[cnp->cn_namelen] = tmpchr; 1670 | return (error); 1671 | 1672 | out: 1673 | if (newfid != NULL) 1674 | p9_client_clunk(newfid); 1675 | cnp->cn_nameptr[cnp->cn_namelen] = tmpchr; 1676 | return (error); 1677 | } 1678 | 1679 | /* Create hard link */ 1680 | static int 1681 | virtfs_link(struct vop_link_args *ap) 1682 | { 1683 | struct vnode *vp; 1684 | struct vnode *tdvp; 1685 | struct componentname *cnp; 1686 | struct virtfs_node *dnp; 1687 | struct virtfs_node *np; 1688 | struct virtfs_inode *inode; 1689 | struct virtfs_session *vses; 1690 | struct p9_fid *dvfid, *oldvfid; 1691 | int error; 1692 | 1693 | vp = ap->a_vp; 1694 | tdvp = ap->a_tdvp; 1695 | cnp = ap->a_cnp; 1696 | dnp = VIRTFS_VTON(tdvp); 1697 | np = VIRTFS_VTON(vp); 1698 | inode = &np->inode; 1699 | vses = np->virtfs_ses; 1700 | error = 0; 1701 | 1702 | p9_debug(VOPS,"virtfs_hardlink\n"); 1703 | p9_debug(VOPS, "%s: tdvp %p vp %p\n", __func__, tdvp, vp); 1704 | 1705 | dvfid = virtfs_get_fid(vses->clnt, dnp, VFID, &error); 1706 | if (error != 0) 1707 | return error; 1708 | oldvfid = virtfs_get_fid(vses->clnt, np, VFID, &error); 1709 | if (error != 0) 1710 | return error; 1711 | 1712 | error = p9_create_hardlink(dvfid, oldvfid, cnp->cn_nameptr); 1713 | if (error != 0) 1714 | return (error); 1715 | /* Increment ref count on the inode */ 1716 | VIRTFS_INCR_LINKS(inode); 1717 | 1718 | return (0); 1719 | } 1720 | 1721 | /* Read contents of the symbolic link */ 1722 | static int 1723 | virtfs_readlink(struct vop_readlink_args *ap) 1724 | { 1725 | struct vnode *vp; 1726 | struct uio *uio; 1727 | struct virtfs_node *dnp; 1728 | struct virtfs_session *vses; 1729 | struct p9_fid *dvfid; 1730 | int error, len; 1731 | char *target; 1732 | 1733 | vp = ap->a_vp; 1734 | uio = ap->a_uio; 1735 | dnp = VIRTFS_VTON(vp); 1736 | vses = dnp->virtfs_ses; 1737 | error = 0; 1738 | 1739 | p9_debug(VOPS, "virtfs_readlink \n"); 1740 | 1741 | dvfid = virtfs_get_fid(vses->clnt, dnp, VFID, &error); 1742 | if (error != 0) 1743 | return error; 1744 | 1745 | error = p9_readlink(dvfid, &target); 1746 | if (error != 0) 1747 | return (error); 1748 | 1749 | len = strlen(target); 1750 | error = uiomove(target, len, uio); 1751 | 1752 | return (0); 1753 | } 1754 | 1755 | /* 1756 | * Iterate through a directory. An entire 8k data is read into the I/O buffer. 1757 | * This buffer is parsed to make dir entries and fed to the user buffer to 1758 | * complete it to the VFS. 1759 | */ 1760 | static int 1761 | virtfs_readdir(struct vop_readdir_args *ap) 1762 | { 1763 | struct uio *uio; 1764 | struct vnode *vp; 1765 | struct dirent cde; 1766 | int64_t offset; 1767 | uint64_t diroffset; 1768 | struct virtfs_node *np; 1769 | int error; 1770 | int32_t count; 1771 | struct p9_client *clnt; 1772 | struct p9_dirent dent; 1773 | char *io_buffer; 1774 | struct p9_fid *vofid; 1775 | 1776 | uio = ap->a_uio; 1777 | vp = ap->a_vp; 1778 | np = VIRTFS_VTON(ap->a_vp); 1779 | offset = 0; 1780 | diroffset = 0; 1781 | error = 0; 1782 | count = 0; 1783 | clnt = np->virtfs_ses->clnt; 1784 | 1785 | if (ap->a_uio->uio_iov->iov_len <= 0) 1786 | return (EINVAL); 1787 | 1788 | if (vp->v_type != VDIR) 1789 | return (ENOTDIR); 1790 | 1791 | vofid = virtfs_get_fid(clnt, np, VOFID, &error); 1792 | if (vofid == NULL) { 1793 | p9_debug(ERROR, "Reading with NULL FID\n"); 1794 | return EBADF; 1795 | } 1796 | 1797 | p9_debug(VOPS, "virtfs_readdir resid %zd\n",uio->uio_resid); 1798 | 1799 | io_buffer = uma_zalloc(virtfs_io_buffer_zone, M_WAITOK); 1800 | 1801 | /* We haven't reached the end yet. read more. */ 1802 | diroffset = uio->uio_offset; 1803 | while (uio->uio_resid >= sizeof(struct dirent)) { 1804 | /* 1805 | * We need to read more data as what is indicated by filesize because 1806 | * filesize is based on data stored in struct dirent structure but 1807 | * we read data in struct p9_dirent format which has different size. 1808 | * Hence we read max data(VIRTFS_IOUNIT) everytime from host, convert 1809 | * it into struct dirent structure and send it back. 1810 | */ 1811 | count = VIRTFS_IOUNIT; 1812 | bzero(io_buffer, VIRTFS_MTU); 1813 | count = p9_client_readdir(vofid, (char *)io_buffer, 1814 | diroffset, count); 1815 | 1816 | if (count == 0) 1817 | break; 1818 | 1819 | if (count < 0) { 1820 | error = EIO; 1821 | goto out; 1822 | } 1823 | 1824 | offset = 0; 1825 | while (offset + QEMU_DIRENTRY_SZ <= count) { 1826 | 1827 | /* 1828 | * Read and make sense out of the buffer in one dirent 1829 | * This is part of 9p protocol read. This reads one p9_dirent, 1830 | * appends it to dirent(FREEBSD specifc) and continues to parse the buffer. 1831 | */ 1832 | bzero(&dent, sizeof(dent)); 1833 | offset = p9_dirent_read(clnt, io_buffer, offset, count, 1834 | &dent); 1835 | if (offset < 0 || offset > count) { 1836 | error = EIO; 1837 | goto out; 1838 | } 1839 | 1840 | bzero(&cde, sizeof(cde)); 1841 | strncpy(cde.d_name, dent.d_name, dent.len); 1842 | cde.d_fileno = dent.qid.path; 1843 | cde.d_type = dent.d_type; 1844 | cde.d_namlen = dent.len; 1845 | cde.d_reclen = GENERIC_DIRSIZ(&cde); 1846 | 1847 | /* 1848 | * If there isn't enough space in the uio to return a 1849 | * whole dirent, break off read 1850 | */ 1851 | if (uio->uio_resid < GENERIC_DIRSIZ(&cde)) 1852 | break; 1853 | 1854 | /* Transfer */ 1855 | error = uiomove(&cde, GENERIC_DIRSIZ(&cde), uio); 1856 | if (error != 0) { 1857 | error = EIO; 1858 | goto out; 1859 | } 1860 | diroffset = dent.d_off; 1861 | } 1862 | } 1863 | /* Pass on last transferred offset */ 1864 | uio->uio_offset = diroffset; 1865 | 1866 | out: 1867 | uma_zfree(virtfs_io_buffer_zone, io_buffer); 1868 | 1869 | return (error); 1870 | } 1871 | 1872 | /* 1873 | * The I/O buffer is mapped to a uio and a client_write/client_read is performed 1874 | * the same way as virtfs_read and virtfs_write. 1875 | */ 1876 | static int 1877 | virtfs_strategy(struct vop_strategy_args *ap) 1878 | { 1879 | struct vnode *vp; 1880 | struct buf *bp; 1881 | struct uio *uiov; 1882 | struct iovec io; 1883 | int error; 1884 | uint64_t off, offset; 1885 | uint64_t filesize; 1886 | uint64_t resid; 1887 | uint32_t count; 1888 | int64_t ret; 1889 | struct virtfs_node *np; 1890 | struct virtfs_session *vses; 1891 | char *io_buffer; 1892 | struct p9_fid *vofid; 1893 | 1894 | vp = ap->a_vp; 1895 | bp = ap->a_bp; 1896 | error = 0; 1897 | np = VIRTFS_VTON(vp); 1898 | vses = np->virtfs_ses; 1899 | 1900 | vofid = virtfs_get_fid(vses->clnt, np, VOFID, &error); 1901 | if (vofid == NULL) { 1902 | p9_debug(ERROR, "Operating on NULL FID\n"); 1903 | return EBADF; 1904 | } 1905 | filesize = np->inode.i_size; 1906 | uiov = malloc(sizeof(struct uio), M_P9UIOV, M_WAITOK); 1907 | uiov->uio_iov = &io; 1908 | uiov->uio_iovcnt = 1; 1909 | uiov->uio_segflg = UIO_SYSSPACE; 1910 | io_buffer = uma_zalloc(virtfs_io_buffer_zone, M_WAITOK | M_ZERO); 1911 | 1912 | if (bp->b_iocmd == BIO_READ) { 1913 | io.iov_len = uiov->uio_resid = bp->b_bcount; 1914 | io.iov_base = bp->b_data; 1915 | uiov->uio_rw = UIO_READ; 1916 | 1917 | switch (vp->v_type) { 1918 | 1919 | case VREG: 1920 | { 1921 | uiov->uio_offset = ((off_t)bp->b_blkno) * DEV_BSIZE; 1922 | 1923 | if (uiov->uio_resid) { 1924 | int left = uiov->uio_resid; 1925 | int nread = bp->b_bcount - left; 1926 | 1927 | if (left > 0) 1928 | bzero((char *)bp->b_data + nread, left); 1929 | } 1930 | /* where in the file are we to start reading */ 1931 | offset = uiov->uio_offset; 1932 | if (uiov->uio_offset >= filesize) 1933 | goto out; 1934 | 1935 | while ((resid = uiov->uio_resid) > 0) { 1936 | if (offset >= filesize) 1937 | break; 1938 | count = min(filesize - uiov->uio_offset, resid); 1939 | if (count == 0) 1940 | break; 1941 | 1942 | p9_debug(VOPS, "virtfs_strategy read called %#zx at %#jx\n", 1943 | uiov->uio_resid, (uintmax_t)uiov->uio_offset); 1944 | 1945 | /* Copy count bytes into the uio */ 1946 | ret = p9_client_read(vofid, offset, count, io_buffer); 1947 | error = uiomove(io_buffer, ret, uiov); 1948 | 1949 | if (error != 0) 1950 | goto out; 1951 | offset += ret; 1952 | } 1953 | break; 1954 | } 1955 | default: 1956 | printf("vfs: type %x unexpected\n", vp->v_type); 1957 | break; 1958 | } 1959 | } else { 1960 | if (bp->b_dirtyend > bp->b_dirtyoff) { 1961 | io.iov_len = uiov->uio_resid = bp->b_dirtyend - bp->b_dirtyoff; 1962 | uiov->uio_offset = ((off_t)bp->b_blkno) * PAGE_SIZE + bp->b_dirtyoff; 1963 | io.iov_base = (char *)bp->b_data + bp->b_dirtyoff; 1964 | uiov->uio_rw = UIO_WRITE; 1965 | 1966 | if (uiov->uio_offset < 0) { 1967 | error = EINVAL; 1968 | goto out; 1969 | } 1970 | 1971 | if (uiov->uio_resid == 0) 1972 | goto out; 1973 | 1974 | resid = uiov->uio_resid; 1975 | offset = uiov->uio_offset; 1976 | error = 0; 1977 | 1978 | while ((resid = uiov->uio_resid) > 0) { 1979 | off = 0; 1980 | count = MIN(resid, VIRTFS_IOUNIT); 1981 | error = uiomove(io_buffer, count, uiov); 1982 | if (error != 0) { 1983 | goto out; 1984 | } 1985 | 1986 | while (count > 0) { 1987 | /* Copy count bytes from the uio */ 1988 | ret = p9_client_write(vofid, offset, count, 1989 | io_buffer + off); 1990 | if (ret < 0) 1991 | goto out; 1992 | 1993 | p9_debug(VOPS, "virtfs_strategy write called %#zx at %#jx\n", 1994 | uiov->uio_resid, (uintmax_t)uiov->uio_offset); 1995 | off += ret; 1996 | offset += ret; 1997 | count -= ret; 1998 | } 1999 | } 2000 | 2001 | /* Update the fields in the node to reflect the change */ 2002 | if (filesize < uiov->uio_offset + uiov->uio_resid) { 2003 | np->inode.i_size = uiov->uio_offset + uiov->uio_resid; 2004 | vnode_pager_setsize(vp, uiov->uio_offset + uiov->uio_resid); 2005 | /* update the modified timers. */ 2006 | virtfs_itimes(vp); 2007 | } 2008 | } else { 2009 | bp->b_resid = 0; 2010 | goto out1; 2011 | } 2012 | } 2013 | out: 2014 | /* Set the error */ 2015 | if (error != 0) { 2016 | bp->b_error = error; 2017 | bp->b_ioflags |= BIO_ERROR; 2018 | } 2019 | bp->b_resid = uiov->uio_resid; 2020 | out1: 2021 | bufdone(bp); 2022 | uma_zfree(virtfs_io_buffer_zone, io_buffer); 2023 | free(uiov, M_P9UIOV); 2024 | 2025 | return (error); 2026 | } 2027 | 2028 | /* Rename a file */ 2029 | static int 2030 | virtfs_rename(struct vop_rename_args *ap) 2031 | { 2032 | struct vnode *tvp; 2033 | struct vnode *tdvp; 2034 | struct vnode *fvp; 2035 | struct vnode *fdvp; 2036 | struct componentname *tcnp; 2037 | struct componentname *fcnp; 2038 | struct virtfs_node *tdnode; 2039 | struct virtfs_node *fdnode; 2040 | struct virtfs_inode *fdinode; 2041 | struct virtfs_node *fnode; 2042 | struct virtfs_inode *finode; 2043 | struct virtfs_session *vses; 2044 | struct virtfs_node *tnode; 2045 | struct virtfs_inode *tinode; 2046 | struct p9_fid *olddirvfid, *newdirvfid ; 2047 | int error; 2048 | 2049 | tvp = ap->a_tvp; 2050 | tdvp = ap->a_tdvp; 2051 | fvp = ap->a_fvp; 2052 | fdvp = ap->a_fdvp; 2053 | tcnp = ap->a_tcnp; 2054 | fcnp = ap->a_fcnp; 2055 | tdnode = VIRTFS_VTON(tdvp); 2056 | fdnode = VIRTFS_VTON(fdvp); 2057 | fdinode = &fdnode->inode; 2058 | fnode = VIRTFS_VTON(fvp); 2059 | finode = &fnode->inode; 2060 | vses = fnode->virtfs_ses; 2061 | error = 0; 2062 | 2063 | p9_debug(VOPS, "virtfs_rename\n "); 2064 | 2065 | /* Check for cross mount operation */ 2066 | if (fvp->v_mount != tdvp->v_mount || 2067 | (tvp && (fvp->v_mount != tvp->v_mount))) { 2068 | error = EXDEV; 2069 | goto out; 2070 | } 2071 | 2072 | /* warning if you are renaming to the same name */ 2073 | if (fvp == tvp) 2074 | error = 0; 2075 | 2076 | olddirvfid = virtfs_get_fid(vses->clnt, fdnode, VFID, &error); 2077 | if (error != 0) 2078 | goto out; 2079 | newdirvfid = virtfs_get_fid(vses->clnt, tdnode, VFID, &error); 2080 | if (error != 0) 2081 | goto out; 2082 | 2083 | error = p9_client_renameat(olddirvfid, fcnp->cn_nameptr, newdirvfid, tcnp->cn_nameptr); 2084 | if (error != 0) 2085 | goto out; 2086 | 2087 | /* 2088 | * decrement the link count on the "from" file whose name is going 2089 | * to be changed if its a directory 2090 | */ 2091 | if (fvp->v_type == VDIR) { 2092 | if (tvp && tvp->v_type == VDIR) 2093 | cache_purge(tdvp); 2094 | VIRTFS_DECR_LINKS(fdinode); 2095 | cache_purge(fdvp); 2096 | } 2097 | 2098 | /* Taking exclusive lock on the from node before decrementing the link count */ 2099 | if ((error = vn_lock(fvp, LK_EXCLUSIVE)) != 0) 2100 | goto out; 2101 | VIRTFS_DECR_LINKS(finode); 2102 | #if __FreeBSD_version >= 1300074 2103 | VOP_UNLOCK(fvp); 2104 | #else 2105 | VOP_UNLOCK(fvp, 0); 2106 | #endif 2107 | 2108 | if (tvp) { 2109 | tnode = VIRTFS_VTON(tvp); 2110 | tinode = &tnode->inode; 2111 | VIRTFS_DECR_LINKS(tinode); 2112 | } 2113 | 2114 | out: 2115 | if (tdvp == tvp) 2116 | vrele(tdvp); 2117 | else 2118 | vput(tdvp); 2119 | if (tvp) 2120 | vput(tvp); 2121 | vrele(fdvp); 2122 | vrele(fvp); 2123 | return error; 2124 | } 2125 | 2126 | 2127 | struct vop_vector virtfs_vnops = { 2128 | .vop_default = &default_vnodeops, 2129 | .vop_lookup = virtfs_lookup, 2130 | .vop_open = virtfs_open, 2131 | .vop_close = virtfs_close, 2132 | .vop_access = virtfs_access, 2133 | .vop_getattr = virtfs_getattr_dotl, 2134 | .vop_setattr = virtfs_setattr_dotl, 2135 | .vop_reclaim = virtfs_reclaim, 2136 | .vop_inactive = virtfs_inactive, 2137 | .vop_readdir = virtfs_readdir, 2138 | .vop_create = virtfs_create, 2139 | .vop_mknod = virtfs_mknod, 2140 | .vop_read = virtfs_read, 2141 | .vop_write = virtfs_write, 2142 | .vop_remove = virtfs_remove, 2143 | .vop_mkdir = virtfs_mkdir, 2144 | .vop_rmdir = virtfs_rmdir, 2145 | .vop_strategy = virtfs_strategy, 2146 | .vop_symlink = virtfs_symlink, 2147 | .vop_rename = virtfs_rename, 2148 | .vop_link = virtfs_link, 2149 | .vop_readlink = virtfs_readlink, 2150 | }; 2151 | #if __FreeBSD_version >= 1300069 2152 | VFS_VOP_VECTOR_REGISTER(virtfs_vnops); 2153 | #endif -------------------------------------------------------------------------------- /sys/dev/virtio/9pnet/client.c: -------------------------------------------------------------------------------- 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 | /* 28 | * This file contains 9P client functions which prepares message to be sent to 29 | * the server. Every fileop typically has a function defined here to interact 30 | * with the host. 31 | */ 32 | 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | #include "transport.h" 53 | 54 | #define QEMU_HEADER 7 55 | #define VIRTFS_MAX_FID_CNT (1024 * 1024 * 1024) 56 | #define VIRTFS_ROOT_FID_NO 2 57 | #define VIRTFS_MIN_TAG 1 58 | #define VIRTFS_MAX_TAG 65535 59 | #define WSTAT_SIZE 47 60 | #define WSTAT_EXTENSION_SIZE 14 61 | 62 | static MALLOC_DEFINE(M_P9CLNT, "p9_client", "P9 Client structure for virfs"); 63 | static uma_zone_t virtfs_fid_zone; 64 | static uma_zone_t virtfs_req_zone; 65 | static uma_zone_t virtfs_buf_zone; 66 | int p9_debug_level = 0; 67 | SYSCTL_INT(_vfs, OID_AUTO, p9_debug_level, CTLFLAG_RW, 68 | &p9_debug_level, 0, "Debug prints enabling switch for VirtFS"); 69 | 70 | static struct p9_req_t *p9_get_request(struct p9_client *c, int *error); 71 | static struct p9_req_t *p9_client_request( 72 | struct p9_client *c, int8_t type, int *error, const char *fmt, ...); 73 | 74 | inline int 75 | p9_is_proto_dotl(struct p9_client *clnt) 76 | { 77 | 78 | return (clnt->proto_version == p9_proto_2000L); 79 | } 80 | 81 | inline int 82 | p9_is_proto_dotu(struct p9_client *clnt) 83 | { 84 | 85 | return (clnt->proto_version == p9_proto_2000u); 86 | } 87 | 88 | /* Parse mount options into client structure */ 89 | static int 90 | p9_parse_opts(struct mount *mp, struct p9_client *clnt) 91 | { 92 | char *trans; 93 | int error; 94 | 95 | error = 0; 96 | /* These are defaults for now */ 97 | clnt->proto_version = p9_proto_2000L; 98 | clnt->msize = 8192; 99 | 100 | trans = vfs_getopts(mp->mnt_optnew, "trans", &error); 101 | if (error != 0) 102 | return (error); 103 | 104 | p9_debug(TRANS, " Attaching to the %s transport \n", trans); 105 | /* Get the default trans callback */ 106 | clnt->trans_mod = p9_get_default_trans(); 107 | 108 | return (error); 109 | } 110 | 111 | /* Allocate buffer for sending request and getting responses */ 112 | static struct p9_buffer * 113 | p9_buffer_alloc(int alloc_msize) 114 | { 115 | struct p9_buffer *fc; 116 | 117 | fc = uma_zalloc(virtfs_buf_zone, M_WAITOK | M_ZERO); 118 | fc->capacity = alloc_msize; 119 | fc->offset = 0; 120 | fc->size = 0; 121 | fc->sdata = (char *) fc + sizeof(struct p9_buffer); 122 | 123 | return (fc); 124 | } 125 | 126 | /* Free memory used by request and repsonse buffers */ 127 | static void 128 | p9_buffer_free(struct p9_buffer **buf) 129 | { 130 | 131 | /* Free the sdata buffers first, then the whole structure*/ 132 | uma_zfree(virtfs_buf_zone, *buf); 133 | *buf = NULL; 134 | } 135 | 136 | /* Free the request */ 137 | static void 138 | p9_free_req(struct p9_client *clnt, struct p9_req_t *req) 139 | { 140 | 141 | if (req->tc != NULL) { 142 | if (req->tc->tag != P9_NOTAG) 143 | p9_tag_destroy(clnt, req->tc->tag); 144 | p9_buffer_free(&req->tc); 145 | } 146 | 147 | if (req->rc != NULL) 148 | p9_buffer_free(&req->rc); 149 | 150 | req->tc = NULL; 151 | req->rc = NULL; 152 | uma_zfree(virtfs_req_zone, req); 153 | } 154 | 155 | /* Allocate a request by tag */ 156 | static struct p9_req_t * 157 | p9_get_request(struct p9_client *clnt, int *error) 158 | { 159 | struct p9_req_t *req; 160 | int alloc_msize; 161 | uint16_t tag; 162 | 163 | alloc_msize = VIRTFS_MTU; 164 | 165 | req = uma_zalloc(virtfs_req_zone, M_WAITOK | M_ZERO); 166 | req->tc = p9_buffer_alloc(alloc_msize); 167 | req->rc = p9_buffer_alloc(alloc_msize); 168 | 169 | tag = p9_tag_create(clnt); 170 | if (tag == P9_NOTAG) { 171 | *error = EAGAIN; 172 | req->tc->tag = P9_NOTAG; 173 | p9_free_req(clnt, req); 174 | return NULL; 175 | } 176 | req->tc->tag = tag; 177 | return (req); 178 | } 179 | 180 | /* Parse header arguments of the response buffer */ 181 | static int 182 | p9_parse_receive(struct p9_buffer *buf, struct p9_client *clnt) 183 | { 184 | int8_t type; 185 | int16_t tag; 186 | int32_t size; 187 | int error; 188 | 189 | buf->offset = 0; 190 | 191 | /* This value is set by QEMU for the header.*/ 192 | if (buf->size == 0) 193 | buf->size = QEMU_HEADER; 194 | 195 | /* This is the initial header. Parse size, type, and tag .*/ 196 | error = p9_buf_readf(buf, 0, "dbw", &size, &type, &tag); 197 | if (error != 0) 198 | goto out; 199 | 200 | buf->size = size; 201 | buf->id = type; 202 | buf->tag = tag; 203 | p9_debug(TRANS, "size=%d type: %d tag: %d\n", buf->size, buf->id, 204 | buf->tag); 205 | out: 206 | return (error); 207 | } 208 | 209 | /* Check 9P response for any errors returned and process it */ 210 | static int 211 | p9_client_check_return(struct p9_client *c, struct p9_req_t *req) 212 | { 213 | int error; 214 | int ecode; 215 | char *ename; 216 | 217 | /* Check what we have in the receive bufer .*/ 218 | error = p9_parse_receive(req->rc, c); 219 | if (error != 0) 220 | goto out; 221 | 222 | /* 223 | * No error, We are done with the preprocessing. Return to the caller 224 | * and process the actual data. 225 | */ 226 | if (req->rc->id != P9PROTO_RERROR && req->rc->id != P9PROTO_RLERROR) 227 | return (0); 228 | 229 | /* 230 | * Interpreting the error is done in different ways for Linux and 231 | * Unix version. Make sure you interpret it right. 232 | */ 233 | if (req->rc->id == P9PROTO_RERROR) { 234 | error = p9_buf_readf(req->rc, c->proto_version, "s?d", &ename, &ecode); 235 | } else if (req->rc->id == P9PROTO_RLERROR) { 236 | error = p9_buf_readf(req->rc, c->proto_version, "d", &ecode); 237 | } else { 238 | goto out; 239 | } 240 | if (error != 0) 241 | goto out; 242 | 243 | /* if there was an ecode error make this the err now */ 244 | error = ecode; 245 | 246 | /* 247 | * Note this is still not completely an error, as lookups for files 248 | * not present can hit this and return. Hence it is made a debug print. 249 | */ 250 | if (error != 0) 251 | if (req->rc->id == P9PROTO_RERROR) { 252 | p9_debug(TRANS, "<<< RERROR (%d) %s\n", error, ename); 253 | } else if (req->rc->id == P9PROTO_RLERROR) { 254 | p9_debug(TRANS, "<<< RLERROR (%d)\n", error); 255 | } 256 | 257 | if (req->rc->id == P9PROTO_RERROR) { 258 | free(ename, M_TEMP); 259 | } 260 | return (error); 261 | 262 | out: 263 | p9_debug(ERROR, "couldn't parse receive buffer error%d\n", error); 264 | return (error); 265 | } 266 | 267 | /* State machine changing helpers */ 268 | void p9_client_disconnect(struct p9_client *clnt) 269 | { 270 | 271 | p9_debug(TRANS, "clnt %p\n", clnt); 272 | clnt->trans_status = VIRTFS_DISCONNECT; 273 | } 274 | 275 | void p9_client_begin_disconnect(struct p9_client *clnt) 276 | { 277 | 278 | p9_debug(TRANS, "clnt %p\n", clnt); 279 | clnt->trans_status = VIRTFS_BEGIN_DISCONNECT; 280 | } 281 | 282 | static struct p9_req_t * 283 | p9_client_prepare_req(struct p9_client *c, int8_t type, 284 | int req_size, int *error, const char *fmt, __va_list ap) 285 | { 286 | struct p9_req_t *req; 287 | 288 | p9_debug(TRANS, "client %p op %d\n", c, type); 289 | 290 | /* 291 | * Before we start with the request, check if its possible to finish 292 | * this request. We are allowed to submit the request only if there 293 | * are no close sessions happening or else there can be race. If the 294 | * status is Disconnected, we stop any requests coming in after that. 295 | */ 296 | if (c->trans_status == VIRTFS_DISCONNECT) { 297 | *error = EIO; 298 | return NULL; 299 | } 300 | 301 | /* Allow only cleanup clunk messages once teardown has started. */ 302 | if ((c->trans_status == VIRTFS_BEGIN_DISCONNECT) && 303 | (type != P9PROTO_TCLUNK)) { 304 | *error = EIO; 305 | return NULL; 306 | } 307 | 308 | /* Allocate buffer for transfering and receiving data from host */ 309 | req = p9_get_request(c, error); 310 | if (*error != 0) { 311 | p9_debug(ERROR, "request allocation failed.\n"); 312 | return NULL; 313 | } 314 | 315 | /* Marshall the data according to QEMU standards */ 316 | *error = p9_buf_prepare(req->tc, type); 317 | if (*error != 0) { 318 | p9_debug(ERROR, "Buf_prepare failed prepare_req %d\n", *error); 319 | goto out; 320 | } 321 | 322 | *error = p9_buf_vwritef(req->tc, c->proto_version, fmt, ap); 323 | if (*error != 0) { 324 | p9_debug(ERROR, "buf_vwrite failed in prepare_req %d \n", 325 | *error); 326 | goto out; 327 | } 328 | 329 | *error = p9_buf_finalize(c, req->tc); 330 | if (*error != 0) { 331 | p9_debug(ERROR, "buf_finalize failed in prepare_req %d \n", 332 | *error); 333 | goto out; 334 | } 335 | 336 | return (req); 337 | out: 338 | p9_free_req(c, req); 339 | return NULL; 340 | } 341 | 342 | /* 343 | * Issue a request and wait for response. The routine takes care of preparing 344 | * the 9P request header to be sent, parsing and checking for error conditions 345 | * in the received buffer. It returns the request structure. 346 | */ 347 | static struct p9_req_t * 348 | p9_client_request(struct p9_client *c, int8_t type, int *error, 349 | const char *fmt, ...) 350 | { 351 | va_list ap; 352 | struct p9_req_t *req; 353 | 354 | va_start(ap, fmt); 355 | req = p9_client_prepare_req(c, type, c->msize, error, fmt, ap); 356 | va_end(ap); 357 | 358 | /* Issue with allocation of request buffer */ 359 | if (*error != 0) 360 | return NULL; 361 | 362 | /* Call into the transport for submission. */ 363 | *error = c->trans_mod->request(c, req); 364 | if (*error != 0) { 365 | p9_debug(ERROR, "request submission failed \n"); 366 | goto out; 367 | } 368 | 369 | /* 370 | * Before we return, pre process the header and the rc buffer before 371 | * calling into the protocol infra to analyze the data in rc. 372 | */ 373 | *error = p9_client_check_return(c, req); 374 | if (*error != 0) 375 | goto out; 376 | 377 | return req; 378 | out: 379 | p9_free_req(c, req); 380 | return NULL; 381 | } 382 | 383 | /* Setup tag contents and structure */ 384 | uint16_t 385 | p9_tag_create(struct p9_client *clnt) 386 | { 387 | int tag; 388 | p9_debug(TRANS, "clnt %p\n", clnt); 389 | 390 | tag = alloc_unr(clnt->tagpool); 391 | /* Alloc_unr returning -1 is an error for no units left */ 392 | if (tag == -1) { 393 | return P9_NOTAG; 394 | } 395 | return (tag); 396 | } 397 | 398 | /* Clean up tag structures */ 399 | void 400 | p9_tag_destroy(struct p9_client *clnt, uint16_t tag) 401 | { 402 | 403 | p9_debug(TRANS, "tag %d\n", tag); 404 | 405 | /* Release to the pool */ 406 | free_unr(clnt->tagpool, tag); 407 | } 408 | 409 | /* Allocate a new fid from the fidpool */ 410 | struct p9_fid * 411 | p9_fid_create(struct p9_client *clnt) 412 | { 413 | struct p9_fid *fid; 414 | 415 | p9_debug(TRANS, "clnt %p\n", clnt); 416 | 417 | fid = uma_zalloc(virtfs_fid_zone, M_WAITOK | M_ZERO); 418 | fid->fid = alloc_unr(clnt->fidpool); 419 | /* Alloc_unr returning -1 is an error for no units left */ 420 | if (fid->fid == -1) { 421 | uma_zfree(virtfs_fid_zone, fid); 422 | return NULL; 423 | } 424 | fid->mode = -1; 425 | fid->uid = -1; 426 | fid->clnt = clnt; 427 | 428 | return (fid); 429 | } 430 | 431 | /* Free the fid by releasing it to fidpool */ 432 | void 433 | p9_fid_destroy(struct p9_fid *fid) 434 | { 435 | struct p9_client *clnt; 436 | 437 | p9_debug(TRANS, "fid %d\n", fid->fid); 438 | clnt = fid->clnt; 439 | /* Release to the pool */ 440 | free_unr(clnt->fidpool, fid->fid); 441 | uma_zfree(virtfs_fid_zone, fid); 442 | } 443 | 444 | /* Request the version of 9P protocol */ 445 | int 446 | p9_client_version(struct p9_client *c) 447 | { 448 | int error; 449 | struct p9_req_t *req; 450 | char *version; 451 | int msize; 452 | 453 | error = 0; 454 | 455 | p9_debug(TRANS, "TVERSION msize %d protocol %d\n", c->msize, 456 | c->proto_version); 457 | 458 | switch (c->proto_version) { 459 | case p9_proto_2000L: 460 | req = p9_client_request(c, P9PROTO_TVERSION, &error, "ds", 461 | c->msize, "9P2000.L"); 462 | break; 463 | case p9_proto_2000u: 464 | req = p9_client_request(c, P9PROTO_TVERSION, &error, "ds", 465 | c->msize, "9P2000.u"); 466 | break; 467 | case p9_proto_legacy: 468 | req = p9_client_request(c, P9PROTO_TVERSION, &error, "ds", 469 | c->msize, "9P2000"); 470 | break; 471 | default: 472 | return (EINVAL); 473 | } 474 | 475 | /* Always return the relevant error code */ 476 | if (error != 0) 477 | return (error); 478 | 479 | error = p9_buf_readf(req->rc, c->proto_version, "ds", &msize, &version); 480 | if (error != 0) { 481 | p9_debug(ERROR, "version error %d\n", error); 482 | goto out; 483 | } 484 | 485 | p9_debug(TRANS, "RVERSION msize %d %s\n", msize, version); 486 | 487 | if (!strncmp(version, "9P2000.L", 8)) 488 | c->proto_version = p9_proto_2000L; 489 | else if (!strncmp(version, "9P2000.u", 8)) 490 | c->proto_version = p9_proto_2000u; 491 | else if (!strncmp(version, "9P2000", 6)) 492 | c->proto_version = p9_proto_legacy; 493 | else { 494 | error = ENOMEM; 495 | goto out; 496 | } 497 | 498 | /* limit the msize .*/ 499 | if (msize < c->msize) 500 | c->msize = msize; 501 | out: 502 | p9_free_req(c, req); 503 | return (error); 504 | } 505 | 506 | /* 507 | * Initialize zones for different things. This is called from Init module 508 | * so that we just have them initalized once. 509 | */ 510 | void 511 | p9_init_zones(void) 512 | { 513 | 514 | /* Create the request and the fid zones */ 515 | virtfs_fid_zone = uma_zcreate("VirtFS fid zone", 516 | sizeof(struct p9_fid), NULL, NULL, NULL, NULL, 0, 0); 517 | 518 | /* Create the request and the fid zones */ 519 | virtfs_req_zone = uma_zcreate("VirtFS req zone", 520 | sizeof(struct p9_req_t), NULL, NULL, NULL, NULL, 0, 0); 521 | 522 | /* Create the buffer zone */ 523 | virtfs_buf_zone = uma_zcreate("VirtFS buf zone", 524 | sizeof(struct p9_buffer) + VIRTFS_MTU, NULL, NULL, 525 | NULL, NULL, 0, 0); 526 | } 527 | 528 | void 529 | p9_destroy_zones(void) 530 | { 531 | 532 | uma_zdestroy(virtfs_fid_zone); 533 | uma_zdestroy(virtfs_req_zone); 534 | uma_zdestroy(virtfs_buf_zone); 535 | } 536 | 537 | /* Return the client to the session in the FS to hold it */ 538 | struct p9_client * 539 | p9_client_create(struct mount *mp, int *error, const char *mount_tag) 540 | { 541 | struct p9_client *clnt; 542 | 543 | clnt = malloc(sizeof(struct p9_client), M_P9CLNT, M_WAITOK | M_ZERO); 544 | 545 | /* Parse should have set trans_mod */ 546 | *error = p9_parse_opts(mp, clnt); 547 | if (*error != 0) 548 | goto out; 549 | 550 | if (clnt->trans_mod == NULL) { 551 | *error = EINVAL; 552 | p9_debug(ERROR, "No transport defined or default transport\n"); 553 | goto out; 554 | } 555 | 556 | /* All the structures from here are protected by the lock clnt-spin */ 557 | clnt->fidpool = new_unrhdr(VIRTFS_ROOT_FID_NO, VIRTFS_MAX_FID_CNT, 558 | NULL); 559 | if (clnt->fidpool == NULL) { 560 | *error = ENOMEM; 561 | p9_debug(ERROR, "Coudlnt initilize fid pool\n"); 562 | goto out; 563 | } 564 | 565 | clnt->tagpool = new_unrhdr(VIRTFS_MIN_TAG, VIRTFS_MAX_TAG, NULL); 566 | if (clnt->tagpool == NULL) { 567 | *error = ENOMEM; 568 | p9_debug(ERROR, "Couldnt initialize tag pool\n"); 569 | goto out; 570 | } 571 | 572 | p9_debug(TRANS, "clnt %p trans %p msize %d protocol %d\n", 573 | clnt, clnt->trans_mod, clnt->msize, clnt->proto_version); 574 | 575 | *error = clnt->trans_mod->create(clnt, mount_tag); 576 | if (*error != 0) { 577 | p9_debug(ERROR, "transport create failed .%d \n",*error); 578 | goto out; 579 | } 580 | 581 | *error = p9_client_version(clnt); 582 | if (*error != 0) 583 | goto out; 584 | 585 | p9_debug(TRANS, "Client creation success .\n"); 586 | return (clnt); 587 | out: 588 | free(clnt, M_P9CLNT); 589 | return NULL; 590 | } 591 | 592 | /* Destroy the client by destroying associated fidpool and tagpool */ 593 | void 594 | p9_client_destroy(struct p9_client *clnt) 595 | { 596 | 597 | p9_debug(TRANS, "clnt %s %p\n", __func__, clnt); 598 | 599 | p9_put_trans(clnt); 600 | 601 | p9_debug(TRANS, "%s : Destroying fidpool\n", __func__); 602 | if (clnt->fidpool != NULL) 603 | delete_unrhdr(clnt->fidpool); 604 | 605 | p9_debug(TRANS, "%s : Destroying tagpool\n", __func__); 606 | if (clnt->tagpool != NULL) 607 | delete_unrhdr(clnt->tagpool); 608 | free(clnt, M_P9CLNT); 609 | } 610 | 611 | /* 612 | * Attach a user to the filesystem. Create a fid for that user to access 613 | * the root of the filesystem. 614 | */ 615 | struct p9_fid * 616 | p9_client_attach(struct p9_client *clnt, struct p9_fid *afid, 617 | const char *uname, uid_t n_uname, const char *aname, int *error) 618 | { 619 | struct p9_req_t *req; 620 | struct p9_fid *fid; 621 | struct p9_qid qid; 622 | 623 | p9_debug(TRANS, "TATTACH \n"); 624 | fid = p9_fid_create(clnt); 625 | if (fid == NULL) { 626 | *error = ENOMEM; 627 | return NULL; 628 | } 629 | fid->uid = n_uname; 630 | 631 | req = p9_client_request(clnt, P9PROTO_TATTACH, error, "ddssd", fid->fid, 632 | P9PROTO_NOFID, uname, aname, n_uname); 633 | if (*error != 0) 634 | goto out; 635 | 636 | *error = p9_buf_readf(req->rc, clnt->proto_version, "Q", &qid); 637 | if (*error != 0) { 638 | p9_debug(ERROR, "buf_readf failed in client_attach %d \n", 639 | *error); 640 | goto out; 641 | } 642 | 643 | p9_debug(TRANS, "RATTACH qid %x.%llx.%x\n", 644 | qid.type, (unsigned long long)qid.path, qid.version); 645 | 646 | memmove(&fid->qid, &qid, sizeof(struct p9_qid)); 647 | p9_free_req(clnt, req); 648 | 649 | return (fid); 650 | out: 651 | if (req != NULL) 652 | p9_free_req(clnt, req); 653 | if (fid != NULL) 654 | p9_fid_destroy(fid); 655 | 656 | return NULL; 657 | } 658 | 659 | /* Delete a file/directory. Corresponding fid will be cluncked too */ 660 | int 661 | p9_client_remove(struct p9_fid *fid) 662 | { 663 | int error; 664 | struct p9_client *clnt; 665 | struct p9_req_t *req; 666 | 667 | p9_debug(TRANS, "TREMOVE fid %d\n", fid->fid); 668 | 669 | error = 0; 670 | clnt = fid->clnt; 671 | 672 | req = p9_client_request(clnt, P9PROTO_TREMOVE, &error, "d", fid->fid); 673 | if (error != 0) { 674 | p9_debug(TRANS, "RREMOVE fid %d\n", fid->fid); 675 | return (error); 676 | } 677 | 678 | p9_free_req(clnt, req); 679 | return (error); 680 | } 681 | 682 | /* Inform the file server that the current file represented by fid is no longer 683 | * needed by the client. Any allocated fid on the server needs a clunk to be 684 | * destroyed. 685 | */ 686 | int 687 | p9_client_clunk(struct p9_fid *fid) 688 | { 689 | int error; 690 | struct p9_client *clnt; 691 | struct p9_req_t *req; 692 | 693 | error = 0; 694 | 695 | if (fid == NULL) { 696 | p9_debug(ERROR, "clunk with NULL fid is bad\n"); 697 | return (0); 698 | } 699 | 700 | p9_debug(TRANS, "TCLUNK fid %d \n", fid->fid); 701 | 702 | clnt = fid->clnt; 703 | req = p9_client_request(clnt, P9PROTO_TCLUNK, &error, "d", fid->fid); 704 | if (req != NULL) { 705 | p9_debug(TRANS, "RCLUNK fid %d\n", fid->fid); 706 | p9_free_req(clnt, req); 707 | } 708 | 709 | p9_fid_destroy(fid); 710 | return (error); 711 | } 712 | 713 | /* 714 | * Client_walk is for searching any component name in a directory. 715 | * This is usually called on lookups. Also when we need a new open fid 716 | * as 9p needs to have an open fid for every file to fileops, we call this 717 | * validate the component of the file and return the newfid(openfid) created. 718 | */ 719 | struct p9_fid * 720 | p9_client_walk(struct p9_fid *oldfid, uint16_t nwnames, char **wnames, 721 | int clone, int *error) 722 | { 723 | struct p9_client *clnt; 724 | struct p9_fid *fid; 725 | struct p9_qid *wqids; 726 | struct p9_req_t *req; 727 | uint16_t nwqids, count; 728 | 729 | clnt = oldfid->clnt; 730 | wqids = NULL; 731 | nwqids = 0; 732 | 733 | /* 734 | * Before, we go and create fid, make sure we are not tearing 735 | * down. Only then we create. 736 | * Allow only cleanup clunk messages once we are starting to teardown. 737 | */ 738 | if (clnt->trans_status != VIRTFS_CONNECT) { 739 | *error = EIO; 740 | return NULL; 741 | } 742 | 743 | if (clone) { 744 | fid = p9_fid_create(clnt); 745 | if (fid == NULL) { 746 | *error = ENOMEM; 747 | return NULL; 748 | } 749 | fid->uid = oldfid->uid; 750 | } else 751 | fid = oldfid; 752 | 753 | p9_debug(TRANS, "TWALK fids %d,%d nwnames %u wname %s\n", 754 | oldfid->fid, fid->fid, nwnames, wnames ? wnames[nwnames-1] : NULL); 755 | 756 | /* 757 | * The newfid is for the component in search. We are preallocating as 758 | * qemu on other side allocates or returns a fid if it sees a match 759 | */ 760 | req = p9_client_request(clnt, P9PROTO_TWALK, error, "ddT", oldfid->fid, 761 | fid->fid, wnames, nwnames); 762 | if (*error != 0) { 763 | if (fid != oldfid) 764 | p9_fid_destroy(fid); 765 | return NULL; 766 | } 767 | 768 | *error = p9_buf_readf(req->rc, clnt->proto_version, "R", &nwqids, 769 | &wqids); 770 | if (*error != 0) 771 | goto out; 772 | 773 | p9_debug(TRANS, "RWALK nwqid %d:\n", nwqids); 774 | 775 | if (nwqids != nwnames) { 776 | *error = ENOENT; 777 | goto out; 778 | } 779 | 780 | for (count = 0; count < nwqids; count++) 781 | p9_debug(TRANS, "[%d] %x.%llx.%x\n", count, wqids[count].type, 782 | (unsigned long long)wqids[count].path, 783 | wqids[count].version); 784 | 785 | if (nwnames) 786 | memmove(&fid->qid, &wqids[nwqids - 1], sizeof(struct p9_qid)); 787 | else 788 | fid->qid = oldfid->qid; 789 | 790 | p9_free_req(clnt, req); 791 | free(wqids, M_TEMP); 792 | return (fid); 793 | 794 | out: 795 | p9_free_req(clnt, req); 796 | if (wqids) 797 | free(wqids, M_TEMP); 798 | if (fid && fid != oldfid) 799 | p9_client_clunk(fid); 800 | return NULL; 801 | } 802 | 803 | /* Open a file with given fid and mode */ 804 | int 805 | p9_client_open(struct p9_fid *fid, int mode) 806 | { 807 | int error, mtu; 808 | struct p9_client *clnt; 809 | struct p9_req_t *req; 810 | 811 | error = 0; 812 | clnt = fid->clnt; 813 | mtu = 0; 814 | 815 | p9_debug(TRANS, "%s fid %d mode %d\n", 816 | p9_is_proto_dotl(clnt) ? "TLOPEN" : "TOPEN", fid->fid, mode); 817 | 818 | if (fid->mode != -1) 819 | return (EINVAL); 820 | 821 | if (p9_is_proto_dotl(clnt)) 822 | req = p9_client_request(clnt, P9PROTO_TLOPEN, &error, "dd", 823 | fid->fid, mode); 824 | else 825 | req = p9_client_request(clnt, P9PROTO_TOPEN, &error, "db", 826 | fid->fid, mode); 827 | 828 | if (error != 0) 829 | return (error); 830 | 831 | error = p9_buf_readf(req->rc, clnt->proto_version, "Qd", &fid->qid, 832 | &mtu); 833 | if (error != 0) 834 | goto out; 835 | 836 | p9_debug(TRANS, " %s qid %x.%llx.%x mtu %x\n", 837 | p9_is_proto_dotl(clnt) ? "RLOPEN" : "ROPEN", (fid->qid).type, 838 | (unsigned long long)(fid->qid).path, (fid->qid).version, mtu); 839 | 840 | fid->mode = mode; 841 | fid->mtu = mtu; 842 | out: 843 | p9_free_req(clnt, req); 844 | return (error); 845 | } 846 | 847 | /* Request to get directory entries */ 848 | int 849 | p9_client_readdir(struct p9_fid *fid, char *data, uint64_t offset, 850 | uint32_t count) 851 | { 852 | int error; 853 | uint32_t rsize; 854 | struct p9_client *clnt; 855 | struct p9_req_t *req; 856 | char *dataptr; 857 | 858 | p9_debug(TRANS, "TREADDIR fid %d offset %llu count %d\n", 859 | fid->fid, (unsigned long long) offset, count); 860 | 861 | error = 0; 862 | rsize = fid->mtu; 863 | clnt = fid->clnt; 864 | 865 | if (!rsize || rsize > clnt->msize) 866 | rsize = clnt->msize; 867 | 868 | if (count < rsize) 869 | rsize = count; 870 | 871 | req = p9_client_request(clnt, P9PROTO_TREADDIR, &error, "dqd", 872 | fid->fid, offset, rsize); 873 | 874 | if (error != 0) { 875 | p9_debug(ERROR, "Couldn't allocate req in client_readdir\n"); 876 | return (-error); 877 | } 878 | 879 | error = p9_buf_readf(req->rc, clnt->proto_version, "D", &count, 880 | &dataptr); 881 | if (error != 0) { 882 | p9_debug(ERROR, "buf_readf error in client_readdir \n"); 883 | p9_free_req(clnt, req); 884 | return (-error); 885 | } 886 | 887 | p9_debug(TRANS, "RREADDIR count %u\n", count); 888 | 889 | /* Copy back the data into the input buffer. */ 890 | memmove(data, dataptr, count); 891 | p9_free_req(clnt, req); 892 | return (count); 893 | } 894 | 895 | /* 896 | * Read count bytes from offset for the file fid into the character 897 | * buffer data. This buffer is handed over to VirtFS to process into user 898 | * buffers. Note that this function typically returns the number of bytes read 899 | * so in case of an error we return -error so that we can distinguish between 900 | * error codes and bytes. 901 | */ 902 | int 903 | p9_client_read(struct p9_fid *fid, uint64_t offset, uint32_t count, char *data) 904 | { 905 | struct p9_client *clnt; 906 | struct p9_req_t *req; 907 | char *dataptr; 908 | int error, rsize; 909 | 910 | clnt = fid->clnt; 911 | rsize = fid->mtu; 912 | error = 0; 913 | 914 | p9_debug(TRANS, "TREAD fid %d offset %llu %u\n", 915 | fid->fid, (unsigned long long) offset, count); 916 | 917 | if (!rsize || rsize > clnt->msize) 918 | rsize = clnt->msize; 919 | 920 | if (count < rsize) 921 | rsize = count; 922 | 923 | /* At this stage, we only have 8K buffers so only transfer */ 924 | req = p9_client_request(clnt, P9PROTO_TREAD, &error, "dqd", fid->fid, 925 | offset, rsize); 926 | if (error != 0) { 927 | p9_debug(ERROR, "Coudlnt allocate request in client_read \n"); 928 | return (-error); 929 | } 930 | 931 | error = p9_buf_readf(req->rc, clnt->proto_version, "D", &count, 932 | &dataptr); 933 | if (error != 0) { 934 | p9_debug(ERROR, "p9_buf_readf error in client_read \n"); 935 | goto out; 936 | } 937 | 938 | if (rsize < count) { 939 | p9_debug(TRANS, "RREAD count (%d > %d)\n", count, rsize); 940 | count = rsize; 941 | } 942 | 943 | p9_debug(TRANS, "RREAD count %d\n", count); 944 | 945 | if (count == 0) { 946 | error = -EIO; 947 | p9_debug(ERROR, "EIO error in client_read \n"); 948 | goto out; 949 | } 950 | 951 | /* Copy back the data into the input buffer. */ 952 | memmove(data, dataptr, count); 953 | p9_free_req(clnt, req); 954 | return count; 955 | out: 956 | p9_free_req(clnt, req); 957 | return (-error); 958 | } 959 | 960 | /* 961 | * Write count bytes from buffer to the offset for the file fid 962 | * Note that this function typically returns the number of bytes written 963 | * so in case of an error we return -error so that we can distinguish between 964 | * error codes and bytes. 965 | */ 966 | 967 | int 968 | p9_client_write(struct p9_fid *fid, uint64_t offset, uint32_t count, char *data) 969 | { 970 | struct p9_client *clnt; 971 | struct p9_req_t *req; 972 | int ret, error, rsize; 973 | 974 | clnt = fid->clnt; 975 | rsize = fid->mtu; 976 | ret = 0; 977 | error = 0; 978 | 979 | p9_debug(TRANS, " TWRITE fid %d offset %llu %u\n", fid->fid, 980 | (unsigned long long) offset, count); 981 | 982 | if (!rsize || rsize > clnt->msize) 983 | rsize = clnt->msize; 984 | 985 | /* Limit set by Qemu ,8168 */ 986 | if (count > rsize) { 987 | count = rsize; 988 | } 989 | 990 | /* 991 | * Doing the Data blob instead. If at all we add the zerocopy, we can 992 | * change it to uio direct copy 993 | */ 994 | req = p9_client_request(clnt, P9PROTO_TWRITE, &error, "dqD", fid->fid, 995 | offset, count, data); 996 | if (error != 0) { 997 | p9_debug(ERROR, "Coudlnt allocate request in client_write \n"); 998 | return (-error); 999 | } 1000 | 1001 | error = p9_buf_readf(req->rc, clnt->proto_version, "d", &ret); 1002 | if (error != 0) { 1003 | p9_debug(ERROR, "p9_buf_readf error in client_write \n"); 1004 | goto out; 1005 | } 1006 | 1007 | p9_debug(TRANS, " RWRITE count %d\n", ret); 1008 | 1009 | if (count < ret) { 1010 | p9_debug(TRANS, "RWRITE count BUG(%d > %d)\n", count, ret); 1011 | ret = count; 1012 | } 1013 | 1014 | if (count == 0) { 1015 | error = EIO; 1016 | p9_debug(ERROR, "EIO error in client_write \n"); 1017 | goto out; 1018 | } 1019 | 1020 | p9_free_req(clnt, req); 1021 | return (ret); 1022 | out: 1023 | p9_free_req(clnt, req); 1024 | return (-error); 1025 | } 1026 | 1027 | 1028 | /* Create file under directory fid, with name, permissions, mode. */ 1029 | int 1030 | p9_client_file_create(struct p9_fid *fid, char *name, uint32_t perm, int mode, 1031 | char *extension) 1032 | { 1033 | int error; 1034 | struct p9_client *clnt; 1035 | struct p9_req_t *req; 1036 | struct p9_qid qid; 1037 | int mtu; 1038 | 1039 | p9_debug(TRANS, "TCREATE fid %d name %s perm %d mode %d\n", 1040 | fid->fid, name, perm, mode); 1041 | 1042 | clnt = fid->clnt; 1043 | error = 0; 1044 | 1045 | if (fid->mode != -1) 1046 | return (EINVAL); 1047 | 1048 | req = p9_client_request(clnt, P9PROTO_TCREATE, &error, "dsdb?s", 1049 | fid->fid, name, perm, mode, extension); 1050 | if (error != 0) 1051 | return (error); 1052 | 1053 | error = p9_buf_readf(req->rc, clnt->proto_version, "Qd", &qid, &mtu); 1054 | if (error != 0) 1055 | goto out; 1056 | 1057 | p9_debug(TRANS, "RCREATE qid %x.%jx.%x mtu %x\n", qid.type, (uintmax_t)qid.path, 1058 | qid.version, mtu); 1059 | fid->mode = mode; 1060 | fid->mtu = mtu; 1061 | 1062 | out: 1063 | p9_free_req(clnt, req); 1064 | return (error); 1065 | } 1066 | 1067 | /* Request file system information of the file system */ 1068 | int 1069 | p9_client_statfs(struct p9_fid *fid, struct p9_statfs *stat) 1070 | { 1071 | int error; 1072 | struct p9_req_t *req; 1073 | struct p9_client *clnt; 1074 | 1075 | error = 0; 1076 | clnt = fid->clnt; 1077 | 1078 | p9_debug(TRANS, "TSTATFS fid %d\n", fid->fid); 1079 | 1080 | req = p9_client_request(clnt, P9PROTO_TSTATFS, &error, "d", fid->fid); 1081 | if (error != 0) { 1082 | return (error); 1083 | } 1084 | 1085 | error = p9_buf_readf(req->rc, clnt->proto_version, "ddqqqqqqd", 1086 | &stat->type, &stat->bsize, &stat->blocks, &stat->bfree, 1087 | &stat->bavail, &stat->files, &stat->ffree, &stat->fsid, 1088 | &stat->namelen); 1089 | 1090 | if (error != 0) 1091 | goto out; 1092 | 1093 | p9_debug(TRANS, " STATFS fid %d type 0x%jx bsize %ju " 1094 | "blocks %ju bfree %ju bavail %ju files %ju ffree %ju " 1095 | "fsid %ju namelen %ju\n", fid->fid, (uintmax_t)stat->type, 1096 | (uintmax_t)stat->bsize, (uintmax_t)stat->blocks, 1097 | (uintmax_t)stat->bfree, (uintmax_t)stat->bavail, 1098 | (uintmax_t)stat->files, (uintmax_t)stat->ffree, 1099 | (uintmax_t)stat->fsid, (uintmax_t)stat->namelen); 1100 | 1101 | out: 1102 | p9_free_req(clnt, req); 1103 | return (error); 1104 | } 1105 | 1106 | /* Rename file referenced by the fid */ 1107 | int 1108 | p9_client_renameat(struct p9_fid *oldfid, char *oldname, struct p9_fid *newfid, 1109 | char *newname) 1110 | { 1111 | int error; 1112 | struct p9_client *clnt; 1113 | struct p9_req_t *req; 1114 | 1115 | p9_debug(TRANS, "p9_client_rename\n"); 1116 | 1117 | error = 0; 1118 | clnt = oldfid->clnt; 1119 | 1120 | /* 1121 | * we are calling the request with TRENAMEAT tag and not TRENAME with 1122 | * the 9p protocol version 9p2000.u as the QEMU version supports this 1123 | * version of renaming 1124 | */ 1125 | req = p9_client_request(clnt, P9PROTO_TRENAMEAT, &error, "dsds", 1126 | oldfid->fid, oldname, newfid->fid, newname); 1127 | 1128 | if (error != 0) 1129 | return (error); 1130 | 1131 | p9_free_req(clnt, req); 1132 | return (error); 1133 | } 1134 | 1135 | /* Request to create symbolic link */ 1136 | int 1137 | p9_create_symlink(struct p9_fid *fid, char *name, char *symtgt, gid_t gid) 1138 | { 1139 | int error; 1140 | struct p9_req_t *req; 1141 | struct p9_client *clnt; 1142 | struct p9_qid qid; 1143 | 1144 | error = 0; 1145 | clnt = fid->clnt; 1146 | 1147 | p9_debug(VOPS, "p9_create_symlink\n"); 1148 | p9_debug(TRANS, "TSYMLINK fid %d\n", fid->fid); 1149 | 1150 | req = p9_client_request(clnt, P9PROTO_TSYMLINK, &error, "dssd", 1151 | fid->fid, name, symtgt, gid); 1152 | 1153 | if (error != 0) 1154 | return (error); 1155 | 1156 | error = p9_buf_readf(req->rc, clnt->proto_version, "Q", &qid); 1157 | if (error != 0) { 1158 | p9_debug(ERROR, "buf_readf failed in create_symlink %d \n", 1159 | error); 1160 | return (error); 1161 | } 1162 | 1163 | p9_debug(TRANS, "RSYMLINK qid %x.%jx.%x\n", 1164 | qid.type, (uintmax_t)qid.path, qid.version); 1165 | 1166 | p9_free_req(clnt, req); 1167 | return (0); 1168 | } 1169 | 1170 | /* Request to create hard link */ 1171 | int 1172 | p9_create_hardlink(struct p9_fid *dfid, struct p9_fid *oldfid, char *name) 1173 | { 1174 | int error; 1175 | struct p9_req_t *req; 1176 | struct p9_client *clnt; 1177 | 1178 | error = 0; 1179 | clnt = dfid->clnt; 1180 | 1181 | p9_debug(VOPS, "p9_create_hardlink\n"); 1182 | p9_debug(TRANS, "TLINK dfid %d oldfid %d\n", dfid->fid, oldfid->fid); 1183 | 1184 | req = p9_client_request(clnt, P9PROTO_TLINK, &error, "dds", dfid->fid, 1185 | oldfid->fid, name); 1186 | if (error != 0) 1187 | return (error); 1188 | 1189 | p9_free_req(clnt, req); 1190 | return (0); 1191 | } 1192 | 1193 | /* Request to read contents of symbolic link */ 1194 | int 1195 | p9_readlink(struct p9_fid *fid, char **target) 1196 | { 1197 | int error; 1198 | struct p9_client *clnt; 1199 | struct p9_req_t *req; 1200 | 1201 | error = 0; 1202 | clnt = fid->clnt; 1203 | 1204 | p9_debug(VOPS, "p9_readlink\n"); 1205 | p9_debug(TRANS, "TREADLINK fid %d\n", fid->fid); 1206 | 1207 | req = p9_client_request(clnt, P9PROTO_TREADLINK, &error, "d", fid->fid); 1208 | if (error != 0) 1209 | return (error); 1210 | 1211 | error = p9_buf_readf(req->rc, clnt->proto_version, "s", target); 1212 | if (error != 0) { 1213 | p9_debug(ERROR, "buf_readf failed in client_readlink %d \n", 1214 | error); 1215 | return (error); 1216 | } 1217 | 1218 | p9_debug(TRANS, "RREADLINK target %s \n", *target); 1219 | 1220 | p9_free_req(clnt, req); 1221 | return (0); 1222 | } 1223 | 1224 | /* Get file attributes of the file referenced by the fid */ 1225 | int 1226 | p9_client_getattr(struct p9_fid *fid, struct p9_stat_dotl *stat_dotl, 1227 | uint64_t request_mask) 1228 | { 1229 | int err; 1230 | struct p9_client *clnt; 1231 | struct p9_req_t *req; 1232 | 1233 | err = 0; 1234 | 1235 | p9_debug(TRANS, "TSTAT fid %d\n mask: %ju", fid->fid, 1236 | (uintmax_t)request_mask); 1237 | 1238 | clnt = fid->clnt; 1239 | req = p9_client_request(clnt, P9PROTO_TGETATTR, &err, "dq", fid->fid, 1240 | request_mask); 1241 | if (req == NULL) { 1242 | p9_debug(ERROR, "Request couldnt be allocated in" 1243 | "client_tgetattr %d\n",err); 1244 | goto error; 1245 | } 1246 | 1247 | err = p9_buf_readf(req->rc, clnt->proto_version, "A", stat_dotl); 1248 | if (err != 0) { 1249 | p9_debug(ERROR, "buf_readf failed in client_tgetattr %d\n", err); 1250 | goto error; 1251 | } 1252 | 1253 | p9_free_req(clnt, req); 1254 | p9_debug(TRANS, " TGETATTR fid %d qid=%x.%jx.%x st_mode %8.8x " 1255 | "uid:%d gid:%d nlink %ju rdev %jx st_size %jx blksize %ju " 1256 | "blocks %ju st_atime_sec:%ju, st_atime_nsec:%ju " 1257 | "st_mtime_sec:%ju, st_mtime_nsec:%ju st_ctime_sec:%ju " 1258 | "st_ctime_nsec:%ju st_btime_sec:%ju, st_btime_nsec:%ju " 1259 | "st_stat:%ju, st_data_version:%ju \n",fid->fid, 1260 | stat_dotl->qid.type, (uintmax_t)stat_dotl->qid.path, 1261 | stat_dotl->qid.version, stat_dotl->st_mode, stat_dotl->st_uid, 1262 | stat_dotl->st_gid, (uintmax_t)stat_dotl->st_nlink, 1263 | (uintmax_t)stat_dotl->st_rdev, (uintmax_t)stat_dotl->st_size, 1264 | (uintmax_t)stat_dotl->st_blksize, 1265 | (uintmax_t)stat_dotl->st_blocks, (uintmax_t)stat_dotl->st_atime_sec, 1266 | (uintmax_t)stat_dotl->st_atime_nsec, (uintmax_t)stat_dotl->st_mtime_sec, 1267 | (uintmax_t)stat_dotl->st_mtime_nsec, (uintmax_t)stat_dotl->st_ctime_sec, 1268 | (uintmax_t)stat_dotl->st_ctime_nsec, (uintmax_t)stat_dotl->st_btime_sec, 1269 | (uintmax_t)stat_dotl->st_btime_nsec, (uintmax_t)stat_dotl->st_gen, 1270 | (uintmax_t)stat_dotl->st_data_version); 1271 | 1272 | return (err); 1273 | 1274 | error: 1275 | if (req != NULL) 1276 | p9_free_req(clnt, req); 1277 | 1278 | return (err); 1279 | } 1280 | 1281 | /* Set file attributes of the file referenced by the fid */ 1282 | int 1283 | p9_client_setattr(struct p9_fid *fid, struct p9_iattr_dotl *p9attr) 1284 | { 1285 | int err; 1286 | struct p9_req_t *req; 1287 | struct p9_client *clnt; 1288 | 1289 | err = 0; 1290 | 1291 | p9_debug(TRANS, "TSETATTR fid %d\n", fid->fid); 1292 | p9_debug(TRANS, 1293 | " valid=%x mode=%x uid=%d gid=%d size=%ju\n" 1294 | " atime_sec=%ju atime_nsec=%ju\n" 1295 | " mtime_sec=%ju mtime_nsec=%ju\n", 1296 | p9attr->valid, p9attr->mode, p9attr->uid, p9attr->gid, 1297 | (uintmax_t)p9attr->size, (uintmax_t)p9attr->atime_sec, 1298 | (uintmax_t)p9attr->atime_nsec, (uintmax_t)p9attr->mtime_sec, 1299 | (uintmax_t)p9attr->mtime_nsec); 1300 | 1301 | clnt = fid->clnt; 1302 | 1303 | /* Any client_request error is converted to req == NULL error*/ 1304 | req = p9_client_request(clnt, P9PROTO_TSETATTR, &err, "dA", fid->fid, 1305 | p9attr); 1306 | 1307 | if (req == NULL) { 1308 | p9_debug(ERROR, "Couldn't allocate request in client_wstat %d\n", 1309 | err); 1310 | goto error; 1311 | } 1312 | 1313 | p9_free_req(clnt, req); 1314 | error: 1315 | return (err); 1316 | } 1317 | 1318 | -------------------------------------------------------------------------------- /sys/dev/virtio/9pnet/protocol.c: -------------------------------------------------------------------------------- 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 | /* 28 | * 9P Protocol Support Code 29 | * This file provides the standard for the FS interactions with the server 30 | * interface as it can understand only this protocol. The details of the 31 | * protocol can be found here 32 | * XXX (link to protocol details page on FreeBSD wiki) 33 | */ 34 | 35 | #include 36 | #include 37 | #include 38 | #include 39 | 40 | #define VIRTFS_MAXLEN 255 41 | 42 | static int p9_buf_writef(struct p9_buffer *buf, int proto_version, 43 | const char *fmt, ...); 44 | static void stat_free(struct p9_wstat *sbuf); 45 | 46 | static void 47 | stat_free(struct p9_wstat *stbuf) 48 | { 49 | 50 | free(stbuf->name, M_TEMP); 51 | free(stbuf->uid, M_TEMP); 52 | free(stbuf->gid, M_TEMP); 53 | free(stbuf->muid, M_TEMP); 54 | free(stbuf->extension, M_TEMP); 55 | } 56 | 57 | static size_t 58 | buf_read(struct p9_buffer *buf, void *data, size_t size) 59 | { 60 | size_t len; 61 | 62 | len = min(buf->size - buf->offset, size); 63 | 64 | memcpy(data, &buf->sdata[buf->offset], len); 65 | buf->offset += len; 66 | 67 | return (size - len); 68 | } 69 | 70 | static size_t 71 | buf_write(struct p9_buffer *buf, const void *data, size_t size) 72 | { 73 | size_t len; 74 | 75 | len = min(buf->capacity - buf->size, size); 76 | 77 | memcpy(&buf->sdata[buf->size], data, len); 78 | buf->size += len; 79 | 80 | return (size - len); 81 | } 82 | 83 | /* 84 | * Main buf_read routine. This copies the data from the buffer into the 85 | * respective values based on the data type. 86 | * Here 87 | * b - int8_t 88 | * w - int16_t 89 | * d - int32_t 90 | * q - int64_t 91 | * s - string 92 | * u - uid 93 | * g - gid 94 | * Q - qid 95 | * S - stat 96 | * A - getattr (9P2000.L) 97 | * D - data blob (int32_t size followed by void *, results are not freed) 98 | * T - array of strings (int16_t count, followed by strings) 99 | * R - array of qids (int16_t count, followed by qids) 100 | * ? - return if version is not .u or .l 101 | */ 102 | static int 103 | p9_buf_vreadf(struct p9_buffer *buf, int proto_version, const char *fmt, 104 | va_list ap) 105 | { 106 | const char *ptr; 107 | int error; 108 | 109 | error = 0; 110 | 111 | for (ptr = fmt; *ptr; ptr++) { 112 | switch (*ptr) { 113 | case 'b': 114 | { 115 | int8_t *val = va_arg(ap, int8_t *); 116 | 117 | if (buf_read(buf, val, sizeof(*val))) 118 | error = EFAULT; 119 | break; 120 | } 121 | case 'w': 122 | { 123 | int16_t *val = va_arg(ap, int16_t *); 124 | 125 | if (buf_read(buf, val, sizeof(*val))) 126 | error = EFAULT; 127 | break; 128 | } 129 | case 'd': 130 | { 131 | int32_t *val = va_arg(ap, int32_t *); 132 | 133 | if (buf_read(buf, val, sizeof(*val))) 134 | error = EFAULT; 135 | break; 136 | } 137 | case 'q': 138 | { 139 | int64_t *val = va_arg(ap, int64_t *); 140 | 141 | if (buf_read(buf, val, sizeof(*val))) 142 | error = EFAULT; 143 | break; 144 | } 145 | case 's': 146 | { 147 | char **sptr_p = va_arg(ap, char **); 148 | uint16_t len; 149 | char *sptr; 150 | 151 | error = buf_read(buf, &len, sizeof(uint16_t)); 152 | if (error) 153 | break; 154 | 155 | sptr = malloc(len + 1, M_TEMP, M_NOWAIT | M_ZERO); 156 | 157 | if (buf_read(buf, sptr, len)) { 158 | error = EFAULT; 159 | free(sptr, M_TEMP); 160 | sptr = NULL; 161 | } else { 162 | (sptr)[len] = 0; 163 | *sptr_p = sptr; 164 | } 165 | break; 166 | } 167 | case 'u': 168 | { 169 | uid_t *val = va_arg(ap, uid_t *); 170 | 171 | if (buf_read(buf, val, sizeof(*val))) 172 | error = EFAULT; 173 | break; 174 | 175 | } 176 | case 'g': 177 | { 178 | gid_t *val = va_arg(ap, gid_t *); 179 | 180 | if (buf_read(buf, val, sizeof(*val))) 181 | error = EFAULT; 182 | break; 183 | 184 | } 185 | case 'Q': 186 | { 187 | struct p9_qid *qid = va_arg(ap, struct p9_qid *); 188 | 189 | error = p9_buf_readf(buf, proto_version, "bdq", 190 | &qid->type, &qid->version, &qid->path); 191 | 192 | break; 193 | } 194 | case 'S': 195 | { 196 | struct p9_wstat *stbuf = va_arg(ap, struct p9_wstat *); 197 | 198 | error = p9_buf_readf(buf, proto_version, "wwdQdddqssss?sddd", 199 | &stbuf->size, &stbuf->type, &stbuf->dev, &stbuf->qid, 200 | &stbuf->mode, &stbuf->atime, &stbuf->mtime, &stbuf->length, 201 | &stbuf->name, &stbuf->uid, &stbuf->gid, &stbuf->muid, 202 | &stbuf->extension, &stbuf->n_uid, &stbuf->n_gid, &stbuf->n_muid); 203 | 204 | if (error != 0) 205 | stat_free(stbuf); 206 | break; 207 | } 208 | case 'A': 209 | { 210 | struct p9_stat_dotl *stbuf = va_arg(ap, struct p9_stat_dotl *); 211 | 212 | error = p9_buf_readf(buf, proto_version, "qQdugqqqqqqqqqqqqqqq", 213 | &stbuf->st_result_mask, &stbuf->qid, &stbuf->st_mode, 214 | &stbuf->st_uid,&stbuf->st_gid, &stbuf->st_nlink, 215 | &stbuf->st_rdev, &stbuf->st_size, &stbuf->st_blksize, 216 | &stbuf->st_blocks, &stbuf->st_atime_sec, 217 | &stbuf->st_atime_nsec, &stbuf->st_mtime_sec, 218 | &stbuf->st_mtime_nsec, &stbuf->st_ctime_sec, 219 | &stbuf->st_ctime_nsec, &stbuf->st_btime_sec, 220 | &stbuf->st_btime_nsec, &stbuf->st_gen, 221 | &stbuf->st_data_version); 222 | 223 | break; 224 | } 225 | case 'D': 226 | { 227 | uint32_t *count = va_arg(ap, uint32_t *); 228 | void **data = va_arg(ap, void **); 229 | 230 | error = buf_read(buf, count, sizeof(uint32_t)); 231 | if (error == 0) { 232 | *count = MIN(*count, buf->size - buf->offset); 233 | *data = &buf->sdata[buf->offset]; 234 | } 235 | break; 236 | } 237 | case 'T': 238 | { 239 | uint16_t *nwname_p = va_arg(ap, uint16_t *); 240 | char ***wnames_p = va_arg(ap, char ***); 241 | uint16_t nwname; 242 | char **wnames; 243 | int i; 244 | 245 | error = buf_read(buf, nwname_p, sizeof(uint16_t)); 246 | if (error != 0) 247 | break; 248 | 249 | nwname = *nwname_p; 250 | wnames = malloc(sizeof(char *) * nwname, M_TEMP, M_NOWAIT | M_ZERO); 251 | 252 | for (i = 0; i < nwname && (error == 0); i++) 253 | error = p9_buf_readf(buf, proto_version, "s", &wnames[i]); 254 | 255 | if (error != 0) { 256 | for (i = 0; i < nwname; i++) 257 | free((wnames)[i], M_TEMP); 258 | free(wnames, M_TEMP); 259 | } else 260 | *wnames_p = wnames; 261 | break; 262 | } 263 | case 'R': 264 | { 265 | uint16_t *nwqid_p = va_arg(ap, uint16_t *); 266 | struct p9_qid **wqids_p = va_arg(ap, struct p9_qid **); 267 | uint16_t nwqid; 268 | struct p9_qid *wqids; 269 | int i; 270 | 271 | wqids = NULL; 272 | error = buf_read(buf, nwqid_p, sizeof(uint16_t)); 273 | if (error != 0) 274 | break; 275 | 276 | nwqid = *nwqid_p; 277 | wqids = malloc(nwqid * sizeof(struct p9_qid), M_TEMP, M_NOWAIT | M_ZERO); 278 | if (wqids == NULL) { 279 | error = ENOMEM; 280 | break; 281 | } 282 | for (i = 0; i < nwqid && (error == 0); i++) 283 | error = p9_buf_readf(buf, proto_version, "Q", &(wqids)[i]); 284 | 285 | if (error != 0) { 286 | free(wqids, M_TEMP); 287 | } else 288 | *wqids_p = wqids; 289 | 290 | break; 291 | } 292 | case '?': 293 | { 294 | if ((proto_version != p9_proto_2000u) && (proto_version != p9_proto_2000L)) 295 | return 0; 296 | break; 297 | } 298 | default: 299 | break; 300 | } 301 | 302 | if (error != 0) 303 | break; 304 | } 305 | 306 | return (error); 307 | } 308 | 309 | /* 310 | * Main buf_write routine. This copies the data into the buffer from the 311 | * respective values based on the data type. 312 | * Here 313 | * b - int8_t 314 | * w - int16_t 315 | * d - int32_t 316 | * q - int64_t 317 | * s - string 318 | * u - uid 319 | * g - gid 320 | * Q - qid 321 | * S - stat 322 | * D - data blob (int32_t size followed by void *, results are not freed) 323 | * T - array of strings (int16_t count, followed by strings) 324 | * W - string of a specific length 325 | * R - array of qids (int16_t count, followed by qids) 326 | * A - setattr (9P2000.L) 327 | * ? - return if version is not .u or .l 328 | */ 329 | 330 | int 331 | p9_buf_vwritef(struct p9_buffer *buf, int proto_version, const char *fmt, 332 | va_list ap) 333 | { 334 | const char *ptr; 335 | int error; 336 | 337 | error = 0; 338 | 339 | for (ptr = fmt; *ptr; ptr++) { 340 | switch (*ptr) { 341 | case 'b': 342 | { 343 | int8_t val = va_arg(ap, int); 344 | 345 | if (buf_write(buf, &val, sizeof(val))) 346 | error = EFAULT; 347 | break; 348 | } 349 | case 'w': 350 | { 351 | int16_t val = va_arg(ap, int); 352 | 353 | if (buf_write(buf, &val, sizeof(val))) 354 | error = EFAULT; 355 | break; 356 | } 357 | case 'd': 358 | { 359 | int32_t val = va_arg(ap, int32_t); 360 | 361 | if (buf_write(buf, &val, sizeof(val))) 362 | error = EFAULT; 363 | break; 364 | } 365 | case 'q': 366 | { 367 | int64_t val = va_arg(ap, int64_t); 368 | 369 | if (buf_write(buf, &val, sizeof(val))) 370 | error = EFAULT; 371 | 372 | break; 373 | } 374 | case 's': 375 | { 376 | const char *sptr = va_arg(ap, const char *); 377 | uint16_t len = 0; 378 | 379 | if (sptr) 380 | len = MIN(strlen(sptr), VIRTFS_MAXLEN); 381 | 382 | error = buf_write(buf, &len, sizeof(uint16_t)); 383 | if (error == 0 && buf_write(buf, sptr, len)) 384 | error = EFAULT; 385 | break; 386 | } 387 | case 'u': 388 | { 389 | uid_t val = va_arg(ap, uid_t); 390 | 391 | if (buf_write(buf, &val, sizeof(val))) 392 | error = EFAULT; 393 | break; 394 | 395 | } 396 | case 'g': 397 | { 398 | gid_t val = va_arg(ap, gid_t); 399 | 400 | if (buf_write(buf, &val, sizeof(val))) 401 | error = EFAULT; 402 | break; 403 | 404 | } 405 | case 'Q': 406 | { 407 | const struct p9_qid *qid = va_arg(ap, const struct p9_qid *); 408 | 409 | error = p9_buf_writef(buf, proto_version, "bdq", 410 | qid->type, qid->version, qid->path); 411 | break; 412 | } 413 | case 'S': 414 | { 415 | struct p9_wstat *stbuf = va_arg(ap, struct p9_wstat *); 416 | 417 | error = p9_buf_writef(buf, proto_version, 418 | "wwdQdddqssss?sddd", stbuf->size, stbuf->type, stbuf->dev, &stbuf->qid, 419 | stbuf->mode, stbuf->atime, stbuf->mtime, stbuf->length, stbuf->name, 420 | stbuf->uid, stbuf->gid, stbuf->muid, stbuf->extension, stbuf->n_uid, 421 | stbuf->n_gid, stbuf->n_muid); 422 | 423 | if (error != 0) 424 | stat_free(stbuf); 425 | 426 | break; 427 | } 428 | case 'D': 429 | { 430 | uint32_t count = va_arg(ap, uint32_t); 431 | void *data = va_arg(ap, void *); 432 | 433 | error = buf_write(buf, &count, sizeof(uint32_t)); 434 | if ((error == 0) && buf_write(buf, data, count)) 435 | error = EFAULT; 436 | 437 | break; 438 | } 439 | case 'T': 440 | { 441 | char **wnames = va_arg(ap, char **); 442 | uint16_t nwnames = va_arg(ap, int); 443 | 444 | error = buf_write(buf, &nwnames, sizeof(uint16_t)); 445 | if (error == 0) { 446 | int i = 0; 447 | for (i = 0; i < nwnames; i++) { 448 | error = p9_buf_writef(buf, proto_version, "s", wnames[i]); 449 | if (error != 0) 450 | break; 451 | } 452 | } 453 | break; 454 | } 455 | case 'W': 456 | { 457 | const char *sptr = va_arg(ap, const char*); 458 | uint16_t len = va_arg(ap, int); 459 | 460 | error = buf_write(buf, &len, sizeof(uint16_t)); 461 | if (error == 0 && buf_write(buf, sptr, len)) 462 | error = EFAULT; 463 | break; 464 | 465 | } 466 | case 'R': 467 | { 468 | uint16_t nwqid = va_arg(ap, int); 469 | struct p9_qid *wqids = va_arg(ap, struct p9_qid *); 470 | int i; 471 | 472 | error = buf_write(buf, &nwqid, sizeof(uint16_t)); 473 | if (error == 0) { 474 | 475 | for (i = 0; i < nwqid; i++) { 476 | error = p9_buf_writef(buf, proto_version, "Q", &wqids[i]); 477 | if (error != 0) 478 | break; 479 | } 480 | } 481 | break; 482 | } 483 | case 'A': 484 | { 485 | struct p9_iattr_dotl *p9attr = va_arg(ap, struct p9_iattr_dotl *); 486 | 487 | error = p9_buf_writef(buf, proto_version, "ddugqqqqq", 488 | p9attr->valid, p9attr->mode, p9attr->uid, 489 | p9attr->gid, p9attr->size, p9attr->atime_sec, 490 | p9attr->atime_nsec, p9attr->mtime_sec, 491 | p9attr->mtime_nsec); 492 | 493 | break; 494 | } 495 | case '?': 496 | { 497 | if ((proto_version != p9_proto_2000u) && (proto_version != p9_proto_2000L)) 498 | return 0; 499 | break; 500 | } 501 | default: 502 | break; 503 | } 504 | 505 | if (error != 0) 506 | break; 507 | } 508 | 509 | return (error); 510 | } 511 | 512 | /* Variadic form of buf_read */ 513 | int 514 | p9_buf_readf(struct p9_buffer *buf, int proto_version, const char *fmt, ...) 515 | { 516 | va_list ap; 517 | int ret; 518 | 519 | va_start(ap, fmt); 520 | ret = p9_buf_vreadf(buf, proto_version, fmt, ap); 521 | va_end(ap); 522 | 523 | return (ret); 524 | } 525 | 526 | /* Variadic form of buf_write */ 527 | static int 528 | p9_buf_writef(struct p9_buffer *buf, int proto_version, const char *fmt, ...) 529 | { 530 | va_list ap; 531 | int ret; 532 | 533 | va_start(ap, fmt); 534 | ret = p9_buf_vwritef(buf, proto_version, fmt, ap); 535 | va_end(ap); 536 | 537 | return ret; 538 | } 539 | 540 | /* File stats read routine for P9 to get attributes of files */ 541 | int 542 | p9stat_read(struct p9_client *clnt, char *buf, size_t len, struct p9_wstat *st) 543 | { 544 | struct p9_buffer msg_buf; 545 | int ret; 546 | 547 | msg_buf.size = len; 548 | msg_buf.capacity = len; 549 | msg_buf.sdata = buf; 550 | msg_buf.offset = 0; 551 | 552 | ret = p9_buf_readf(&msg_buf, clnt->proto_version, "S", st); 553 | if (ret) { 554 | p9_debug(ERROR, "p9stat_read failed: %d\n", ret); 555 | } 556 | 557 | return ret; 558 | } 559 | 560 | /* 561 | * P9_header preparation routine. All p9 buffers have to have this header(QEMU_HEADER) at the 562 | * front of the buffer. 563 | */ 564 | int 565 | p9_buf_prepare(struct p9_buffer *buf, int8_t type) 566 | { 567 | buf->id = type; 568 | return p9_buf_writef(buf, 0, "dbw", 0, type, buf->tag); 569 | } 570 | 571 | /* 572 | * Final write to the buffer, this is the total size of the buffer. Since the buffer length can 573 | * vary with request, this is computed at the end just before sending the request to the driver 574 | */ 575 | int 576 | p9_buf_finalize(struct p9_client *clnt, struct p9_buffer *buf) 577 | { 578 | int size; 579 | int error; 580 | 581 | size = buf->size; 582 | buf->size = 0; 583 | error = p9_buf_writef(buf, 0, "d", size); 584 | buf->size = size; 585 | 586 | p9_debug(PROTO, "size=%d type: %d tag: %d\n", 587 | buf->size, buf->id, buf->tag); 588 | 589 | return error; 590 | } 591 | 592 | /* Reset values of the buffer */ 593 | void 594 | p9_buf_reset(struct p9_buffer *buf) 595 | { 596 | 597 | buf->offset = 0; 598 | buf->size = 0; 599 | } 600 | 601 | /* 602 | * Directory entry read with the buf we have. Call this once we have the buf to parse. 603 | * This buf, obtained from the server, is parsed to make dirent in readdir. 604 | */ 605 | int 606 | p9_dirent_read(struct p9_client *clnt, char *buf, int start, int len, 607 | struct p9_dirent *dent) 608 | { 609 | struct p9_buffer msg_buf; 610 | int ret; 611 | char *nameptr; 612 | uint16_t sle; 613 | 614 | msg_buf.size = len; 615 | msg_buf.capacity = len; 616 | msg_buf.sdata = buf; 617 | msg_buf.offset = start; 618 | 619 | ret = p9_buf_readf(&msg_buf, clnt->proto_version, "Qqbs", &dent->qid, 620 | &dent->d_off, &dent->d_type, &nameptr); 621 | if (ret) { 622 | p9_debug(ERROR, " p9_dirent_read failed: %d\n", ret); 623 | goto out; 624 | } 625 | 626 | sle = strlen(nameptr); 627 | strncpy(dent->d_name, nameptr, sle); 628 | dent->len = sle; 629 | free(nameptr, M_TEMP); 630 | out: 631 | return msg_buf.offset; 632 | } 633 | -------------------------------------------------------------------------------- /sys/dev/virtio/9pnet/trans_virtio.c: -------------------------------------------------------------------------------- 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 | * The Virtio 9P transport driver. This file contains all functions related to 28 | * the virtqueue infrastructure which include creating the virtqueue, host 29 | * interactions, interrupts etc. 30 | */ 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | 42 | #include 43 | 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | #include 50 | 51 | #include "transport.h" 52 | #include "virtio_9p_config.h" 53 | 54 | #define VT9P_MTX(_sc) (&(_sc)->vt9p_mtx) 55 | #define VT9P_LOCK(_sc) mtx_lock(VT9P_MTX(_sc)) 56 | #define VT9P_UNLOCK(_sc) mtx_unlock(VT9P_MTX(_sc)) 57 | #define VT9P_LOCK_INIT(_sc) mtx_init(VT9P_MTX(_sc), \ 58 | "VIRTIO 9P CHAN lock", NULL, MTX_DEF) 59 | #define VT9P_LOCK_DESTROY(_sc) mtx_destroy(VT9P_MTX(_sc)) 60 | #define MAX_SUPPORTED_SGS 20 61 | static MALLOC_DEFINE(M_VIRTFS_MNTTAG, "virtfs_mount_tag", "VirtFS Mounttag"); 62 | struct vt9p_softc { 63 | device_t vt9p_dev; 64 | struct mtx vt9p_mtx; 65 | struct sglist *vt9p_sglist; 66 | struct cv submit_cv; 67 | struct p9_client *client; 68 | struct virtqueue *vt9p_vq; 69 | int max_nsegs; 70 | uint16_t mount_tag_len; 71 | char *mount_tag; 72 | STAILQ_ENTRY(vt9p_softc) chan_next; 73 | }; 74 | 75 | /* Global channel list, Each channel will correspond to a mount point */ 76 | STAILQ_HEAD( ,vt9p_softc) global_chan_list; 77 | struct mtx global_chan_list_mtx; 78 | 79 | static struct virtio_feature_desc virtio_9p_feature_desc[] = { 80 | { VIRTIO_9PNET_F_MOUNT_TAG, "VIRTFS_MOUNT_TAG" }, 81 | { 0, NULL } 82 | }; 83 | 84 | static void 85 | global_chan_list_init(void) 86 | { 87 | 88 | mtx_init(&global_chan_list_mtx, "GLOBAL CHAN LIST LOCK", 89 | NULL, MTX_DEF); 90 | STAILQ_INIT(&global_chan_list); 91 | } 92 | SYSINIT(global_chan_list_init, SI_SUB_KLD, SI_ORDER_FIRST, 93 | global_chan_list_init, NULL); 94 | 95 | /* We don't currently allow canceling of virtio requests */ 96 | static int 97 | vt9p_cancel(struct p9_client *client, struct p9_req_t *req) 98 | { 99 | 100 | return (1); 101 | } 102 | 103 | SYSCTL_NODE(_vfs, OID_AUTO, 9p, CTLFLAG_RW, 0, "9P File System Protocol"); 104 | 105 | /* 106 | * Maximum number of seconds vt9p_request thread sleep waiting for an 107 | * ack from the host, before exiting 108 | */ 109 | static unsigned int vt9p_ackmaxidle = 120; 110 | 111 | SYSCTL_UINT(_vfs_9p, OID_AUTO, ackmaxidle, CTLFLAG_RW, &vt9p_ackmaxidle, 0, 112 | "Maximum time request thread waits for ack from host"); 113 | 114 | /* 115 | * Request handler. This is called for every request submitted to the host 116 | * It basically maps the tc/rc buffers to sg lists and submits the requests 117 | * into the virtqueue. Since we have implemented a synchronous version, the 118 | * submission thread sleeps until the ack in the interrupt wakes it up. Once 119 | * it wakes up, it returns back to the VirtFS layer. The rc buffer is then 120 | * processed and completed to its upper layers. 121 | */ 122 | static int 123 | vt9p_request(struct p9_client *client, struct p9_req_t *req) 124 | { 125 | int error; 126 | struct vt9p_softc *chan; 127 | struct p9_req_t *curreq; 128 | int readable, writable; 129 | struct sglist *sg; 130 | struct virtqueue *vq; 131 | 132 | chan = client->trans; 133 | sg = chan->vt9p_sglist; 134 | vq = chan->vt9p_vq; 135 | 136 | p9_debug(TRANS, "9P debug: virtio request\n"); 137 | 138 | /* Grab the channel lock*/ 139 | VT9P_LOCK(chan); 140 | sglist_reset(sg); 141 | /* Handle out VirtIO ring buffers */ 142 | error = sglist_append(sg, req->tc->sdata, req->tc->size); 143 | if (error != 0) { 144 | p9_debug(ERROR, "sglist append failed\n"); 145 | return (error); 146 | } 147 | readable = sg->sg_nseg; 148 | 149 | error = sglist_append(sg, req->rc->sdata, req->rc->capacity); 150 | if (error != 0) { 151 | p9_debug(ERROR, " sglist append failed\n"); 152 | return (error); 153 | } 154 | writable = sg->sg_nseg - readable; 155 | 156 | req_retry: 157 | error = virtqueue_enqueue(vq, req, sg, readable, writable); 158 | 159 | if (error != 0) { 160 | if (error == ENOSPC) { 161 | /* 162 | * Condvar for the submit queue. Unlock the chan 163 | * since wakeup needs one. 164 | */ 165 | cv_wait(&chan->submit_cv, VT9P_MTX(chan)); 166 | p9_debug(TRANS, "Retry virtio request\n"); 167 | goto req_retry; 168 | } else { 169 | p9_debug(ERROR, "virtio enuqueue failed \n"); 170 | return (EIO); 171 | } 172 | } 173 | 174 | /* We have to notify */ 175 | virtqueue_notify(vq); 176 | 177 | do { 178 | curreq = virtqueue_dequeue(vq, NULL); 179 | if (curreq == NULL) { 180 | /* Nothing to dequeue, sleep until we have something */ 181 | if (msleep(chan, VT9P_MTX(chan), 0, "chan lock", 182 | vt9p_ackmaxidle * hz)) { 183 | /* 184 | * Waited for 120s. No response from host. 185 | * Can't wait for ever.. 186 | */ 187 | p9_debug(ERROR, "Timeout after waiting %u seconds" 188 | "for an ack from host\n", vt9p_ackmaxidle); 189 | VT9P_UNLOCK(chan); 190 | return (EIO); 191 | } 192 | } else { 193 | cv_signal(&chan->submit_cv); 194 | /* We dequeued something, update the reply tag */ 195 | curreq->rc->tag = curreq->tc->tag; 196 | } 197 | } while (req->rc->tag == P9_NOTAG); 198 | 199 | VT9P_UNLOCK(chan); 200 | 201 | p9_debug(TRANS, "virtio request kicked\n"); 202 | 203 | return (0); 204 | } 205 | 206 | /* 207 | * Completion of the request from the virtqueue. This interrupt handler is 208 | * setup at initialization and is called for every completing request. It 209 | * just wakes up the sleeping submission thread. 210 | */ 211 | static void 212 | vt9p_intr_complete(void *xsc) 213 | { 214 | struct vt9p_softc *chan; 215 | struct virtqueue *vq; 216 | 217 | chan = (struct vt9p_softc *)xsc; 218 | vq = chan->vt9p_vq; 219 | 220 | p9_debug(TRANS, "Completing interrupt \n"); 221 | 222 | VT9P_LOCK(chan); 223 | virtqueue_enable_intr(vq); 224 | wakeup(chan); 225 | VT9P_UNLOCK(chan); 226 | } 227 | 228 | /* 229 | * Allocation of the virtqueue with interrupt complete routines. 230 | */ 231 | static int 232 | vt9p_alloc_virtqueue(struct vt9p_softc *sc) 233 | { 234 | struct vq_alloc_info vq_info; 235 | device_t dev; 236 | 237 | dev = sc->vt9p_dev; 238 | 239 | VQ_ALLOC_INFO_INIT(&vq_info, sc->max_nsegs, 240 | vt9p_intr_complete, sc, &sc->vt9p_vq, 241 | "%s request", device_get_nameunit(dev)); 242 | 243 | return (virtio_alloc_virtqueues(dev, 0, 1, &vq_info)); 244 | } 245 | 246 | /* Probe for existence of 9P virtio channels */ 247 | static int 248 | vt9p_probe(device_t dev) 249 | { 250 | 251 | /* If the virtio device type is a 9P device, then we claim and attach it */ 252 | if (virtio_get_device_type(dev) != VIRTIO_ID_9P) 253 | return (ENXIO); 254 | device_set_desc(dev, "VirtIO 9P Transport"); 255 | p9_debug(TRANS, "Probe successful .\n"); 256 | 257 | return (BUS_PROBE_DEFAULT); 258 | } 259 | 260 | static void 261 | vt9p_stop(struct vt9p_softc *sc) 262 | { 263 | 264 | /* Device specific stops .*/ 265 | virtqueue_disable_intr(sc->vt9p_vq); 266 | virtio_stop(sc->vt9p_dev); 267 | } 268 | 269 | /* Detach the 9P virtio PCI device */ 270 | static int 271 | vt9p_detach(device_t dev) 272 | { 273 | struct vt9p_softc *sc; 274 | 275 | sc = device_get_softc(dev); 276 | VT9P_LOCK(sc); 277 | vt9p_stop(sc); 278 | VT9P_UNLOCK(sc); 279 | 280 | if (sc->vt9p_sglist) { 281 | sglist_free(sc->vt9p_sglist); 282 | sc->vt9p_sglist = NULL; 283 | } 284 | if (sc->mount_tag) { 285 | free(sc->mount_tag, M_VIRTFS_MNTTAG); 286 | sc->mount_tag = NULL; 287 | } 288 | mtx_lock(&global_chan_list_mtx); 289 | STAILQ_REMOVE(&global_chan_list, sc, vt9p_softc, chan_next); 290 | mtx_unlock(&global_chan_list_mtx); 291 | 292 | VT9P_LOCK_DESTROY(sc); 293 | cv_destroy(&sc->submit_cv); 294 | 295 | return (0); 296 | } 297 | 298 | /* Attach the 9P virtio PCI device */ 299 | static int 300 | vt9p_attach(device_t dev) 301 | { 302 | struct sysctl_ctx_list *ctx; 303 | struct sysctl_oid *tree; 304 | struct vt9p_softc *chan; 305 | char *mount_tag; 306 | int error; 307 | uint16_t mount_tag_len; 308 | 309 | chan = device_get_softc(dev); 310 | chan->vt9p_dev = dev; 311 | 312 | /* Init the channel lock. */ 313 | VT9P_LOCK_INIT(chan); 314 | /* Initialize the condition variable */ 315 | cv_init(&chan->submit_cv, "Conditional variable for submit queue" ); 316 | chan->max_nsegs = MAX_SUPPORTED_SGS; 317 | chan->vt9p_sglist = sglist_alloc(chan->max_nsegs, M_NOWAIT); 318 | 319 | /* Negotiate the features from the host */ 320 | virtio_set_feature_desc(dev, virtio_9p_feature_desc); 321 | virtio_negotiate_features(dev, VIRTIO_9PNET_F_MOUNT_TAG); 322 | 323 | /* 324 | * If mount tag feature is supported read the mount tag 325 | * from device config 326 | */ 327 | if (virtio_with_feature(dev, VIRTIO_9PNET_F_MOUNT_TAG)) 328 | mount_tag_len = virtio_read_dev_config_2(dev, 329 | offsetof(struct virtio_9pnet_config, mount_tag_len)); 330 | else { 331 | error = EINVAL; 332 | p9_debug(ERROR, "Mount tag feature not supported by host\n"); 333 | goto out; 334 | } 335 | mount_tag = malloc(mount_tag_len + 1, M_VIRTFS_MNTTAG, 336 | M_WAITOK | M_ZERO); 337 | 338 | virtio_read_device_config(dev, 339 | offsetof(struct virtio_9pnet_config, mount_tag), 340 | mount_tag, mount_tag_len); 341 | 342 | mount_tag_len++; 343 | chan->mount_tag_len = mount_tag_len; 344 | chan->mount_tag = mount_tag; 345 | 346 | ctx = device_get_sysctl_ctx(dev); 347 | tree = device_get_sysctl_tree(dev); 348 | SYSCTL_ADD_STRING(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "virtfs_mount_tag", 349 | CTLFLAG_RD, chan->mount_tag, 0, "Mount tag"); 350 | 351 | if (chan->vt9p_sglist == NULL) { 352 | error = ENOMEM; 353 | p9_debug(ERROR, "Cannot allocate sglist\n"); 354 | goto out; 355 | } 356 | 357 | /* We expect one virtqueue, for requests. */ 358 | error = vt9p_alloc_virtqueue(chan); 359 | 360 | if (error != 0) { 361 | p9_debug(ERROR, "Allocating the virtqueue failed \n"); 362 | goto out; 363 | } 364 | 365 | error = virtio_setup_intr(dev, INTR_TYPE_MISC|INTR_MPSAFE); 366 | 367 | if (error != 0) { 368 | p9_debug(ERROR, "Cannot setup virtqueue interrupt\n"); 369 | goto out; 370 | } 371 | error = virtqueue_enable_intr(chan->vt9p_vq); 372 | 373 | if (error != 0) { 374 | p9_debug(ERROR, "Cannot enable virtqueue interrupt\n"); 375 | goto out; 376 | } 377 | 378 | mtx_lock(&global_chan_list_mtx); 379 | /* Insert the channel in global channel list */ 380 | STAILQ_INSERT_HEAD(&global_chan_list, chan, chan_next); 381 | mtx_unlock(&global_chan_list_mtx); 382 | 383 | p9_debug(TRANS, "Attach successfully \n"); 384 | 385 | return (0); 386 | out: 387 | /* Something went wrong, detach the device */ 388 | vt9p_detach(dev); 389 | return (error); 390 | } 391 | 392 | /* 393 | * Allocate a new virtio channel. This sets up a transport channel 394 | * for 9P communication 395 | */ 396 | static int 397 | vt9p_create(struct p9_client *client, const char *mount_tag) 398 | { 399 | struct vt9p_softc *sc, *chan; 400 | 401 | chan = NULL; 402 | 403 | /* 404 | * Find out the corresponding channel for a client from global list 405 | * of channels based on mount tag and attach it to client 406 | */ 407 | mtx_lock(&global_chan_list_mtx); 408 | STAILQ_FOREACH(sc, &global_chan_list, chan_next) { 409 | if (!strcmp(sc->mount_tag, mount_tag)) { 410 | chan = sc; 411 | break; 412 | } 413 | } 414 | mtx_unlock(&global_chan_list_mtx); 415 | 416 | /* 417 | * If chan is already attached to a client then it cannot be used for 418 | * another client. 419 | */ 420 | if (chan && chan->client != NULL) { 421 | p9_debug(TRANS, "Channel busy: used by clnt=%p\n", 422 | chan->client); 423 | return (EBUSY); 424 | } 425 | 426 | /* If we dont have one, for now bail out.*/ 427 | if (chan) { 428 | client->trans = (void *)chan; 429 | chan->client = client; 430 | client->trans_status = VIRTFS_CONNECT; 431 | } else { 432 | p9_debug(TRANS, "No Global channel with mount_tag=%s\n", 433 | mount_tag); 434 | return (EINVAL); 435 | } 436 | 437 | return (0); 438 | } 439 | 440 | static struct p9_trans_module vt9p_trans = { 441 | .name = "virtio", 442 | .create = vt9p_create, 443 | .request = vt9p_request, 444 | .cancel = vt9p_cancel, 445 | .def = 1, 446 | }; 447 | 448 | struct p9_trans_module * 449 | p9_get_default_trans(void) 450 | { 451 | 452 | return &vt9p_trans; 453 | } 454 | 455 | void 456 | p9_put_trans(struct p9_client *clnt) 457 | { 458 | struct vt9p_softc *chan; 459 | 460 | chan = clnt->trans; 461 | 462 | p9_debug(TRANS, "%s: its just a stub \n", __func__); 463 | chan->client = NULL; 464 | } 465 | 466 | 467 | static device_method_t vt9p_mthds[] = { 468 | /* Device methods. */ 469 | DEVMETHOD(device_probe, vt9p_probe), 470 | DEVMETHOD(device_attach, vt9p_attach), 471 | DEVMETHOD(device_detach, vt9p_detach), 472 | DEVMETHOD_END 473 | }; 474 | 475 | static driver_t vt9p_drv = { 476 | "9p_virtio", 477 | vt9p_mthds, 478 | sizeof(struct vt9p_softc) 479 | }; 480 | static devclass_t vt9p_class; 481 | 482 | static int 483 | vt9p_modevent(module_t mod, int type, void *unused) 484 | { 485 | int error; 486 | 487 | error = 0; 488 | 489 | switch (type) { 490 | case MOD_LOAD: 491 | p9_init_zones(); 492 | break; 493 | case MOD_UNLOAD: 494 | p9_destroy_zones(); 495 | break; 496 | case MOD_SHUTDOWN: 497 | break; 498 | default: 499 | error = EOPNOTSUPP; 500 | break; 501 | } 502 | return (error); 503 | } 504 | 505 | DRIVER_MODULE(vt9p, virtio_pci, vt9p_drv, vt9p_class, 506 | vt9p_modevent, 0); 507 | MODULE_VERSION(vt9p, 1); 508 | MODULE_DEPEND(vt9p, virtio, 1, 1, 1); 509 | -------------------------------------------------------------------------------- /sys/dev/virtio/9pnet/transport.h: -------------------------------------------------------------------------------- 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 | /* Transport definitions */ 28 | #ifndef NET_9P_TRANSPORT_H 29 | #define NET_9P_TRANSPORT_H 30 | 31 | /* Tranport module interface */ 32 | struct p9_trans_module { 33 | char *name; /* name of transport */ 34 | int def; /* this transport should be default */ 35 | /* member function to create a new conection on this transport*/ 36 | int (*create)(struct p9_client *, const char *mount_tag); 37 | /* member function to terminate a connection on this transport */ 38 | void (*close) (struct p9_client *); 39 | /* member function to issue a request to the transport*/ 40 | int (*request) (struct p9_client *, struct p9_req_t *req); 41 | /* member function to cancel a request if it has been sent */ 42 | int (*cancel) (struct p9_client *, struct p9_req_t *req); 43 | /* 44 | * member function to notify that a cancelled request will not 45 | * receive a reply 46 | */ 47 | int (*cancelled)(struct p9_client *, struct p9_req_t *req); 48 | }; 49 | 50 | void p9_register_trans(struct p9_trans_module *m); 51 | void p9_unregister_trans(struct p9_trans_module *m); 52 | struct p9_trans_module *p9_get_trans_by_name(char *s); 53 | struct p9_trans_module *p9_get_default_trans(void); 54 | void p9_put_trans(struct p9_client *clnt); 55 | void p9_init_zones(void); 56 | void p9_destroy_zones(void); 57 | 58 | #endif /* NET_9P_TRANSPORT_H */ 59 | -------------------------------------------------------------------------------- /sys/dev/virtio/9pnet/virtio_9p_config.h: -------------------------------------------------------------------------------- 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 | #ifndef __VIRTIO_9P_CONFIG__ 28 | #define __VIRTIO_9P_CONFIG__ 29 | 30 | /* Mount point feature specified in config variable */ 31 | #define VIRTIO_9PNET_F_MOUNT_TAG 1 32 | 33 | struct virtio_9pnet_config { 34 | /* Mount tag length */ 35 | uint16_t mount_tag_len; 36 | /* non NULL terminated tag name */ 37 | uint8_t mount_tag[0]; 38 | } __attribute__((packed)); 39 | #endif /* __VIRTIO_9P_CONFIG__ */ 40 | -------------------------------------------------------------------------------- /sys/dev/virtio/virtio_fs_9p.h: -------------------------------------------------------------------------------- 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 AND CONTRIBUTORS ``AS IS'' AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | * SUCH DAMAGE. 25 | */ 26 | 27 | /* File contains 9P protocol definitions */ 28 | 29 | #ifndef VIRTIO_FS_9P_H 30 | #define VIRTIO_FS_9P_H 31 | 32 | #include 33 | 34 | /* 9P message types */ 35 | enum p9_cmds_t { 36 | P9PROTO_TLERROR = 6, /* not used */ 37 | P9PROTO_RLERROR, /* response for any failed request */ 38 | P9PROTO_TSTATFS = 8, /* file system status request */ 39 | P9PROTO_RSTATFS, /* file system status response */ 40 | P9PROTO_TLOPEN = 12, /* open a file (9P2000.L) */ 41 | P9PROTO_RLOPEN, /* response to opne request (9P2000.L) */ 42 | P9PROTO_TLCREATE = 14, /* prepare for handle for I/O on a new file (9P2000.L) */ 43 | P9PROTO_RLCREATE, /* response with file access information (9P2000.L) */ 44 | P9PROTO_TSYMLINK = 16, /* symlink creation request */ 45 | P9PROTO_RSYMLINK, /* symlink creation response */ 46 | P9PROTO_TMKNOD = 18, /* create a special file object request */ 47 | P9PROTO_RMKNOD, /* create a special file object response */ 48 | P9PROTO_TRENAME = 20, /* rename a file request */ 49 | P9PROTO_RRENAME, /* rename a file response */ 50 | P9PROTO_TREADLINK = 22, /* request to read value of symbolic link */ 51 | P9PROTO_RREADLINK, /* response to read value of symbolic link request */ 52 | P9PROTO_TGETATTR = 24, /* get file attributes request */ 53 | P9PROTO_RGETATTR, /* get file attributes response */ 54 | P9PROTO_TSETATTR = 26, /* set file attributes request */ 55 | P9PROTO_RSETATTR, /* set file attributes response */ 56 | P9PROTO_TXATTRWALK = 30,/* request to read extended attributes */ 57 | P9PROTO_RXATTRWALK, /* response from server with attributes */ 58 | P9PROTO_TXATTRCREATE = 32,/* request to set extended attribute */ 59 | P9PROTO_RXATTRCREATE, /* response from server for setting extended attribute */ 60 | P9PROTO_TREADDIR = 40, /* request to read a directory */ 61 | P9PROTO_RREADDIR, /* response from server for read request */ 62 | P9PROTO_TFSYNC = 50, /* request to flush an cached data to disk */ 63 | P9PROTO_RFSYNC, /* response when cache dat is flushed */ 64 | P9PROTO_TLOCK = 52, /* acquire or release a POSIX record lock */ 65 | P9PROTO_RLOCK, /* response with the status of the lock */ 66 | P9PROTO_TGETLOCK = 54, /* request to check for presence of a POSIX record lock */ 67 | P9PROTO_RGETLOCK, /* response with the details of the lock if acquired */ 68 | P9PROTO_TLINK = 70, /* request to create hard link */ 69 | P9PROTO_RLINK, /* create hard link response */ 70 | P9PROTO_TMKDIR = 72, /* create a directory request */ 71 | P9PROTO_RMKDIR, /* create a directory response */ 72 | P9PROTO_TRENAMEAT = 74, /* request to rename a file or directory */ 73 | P9PROTO_RRENAMEAT, /* reponse to rename request */ 74 | P9PROTO_TUNLINKAT = 76, /* unlink a file or directory */ 75 | P9PROTO_RUNLINKAT, /* reponse to unlink request */ 76 | P9PROTO_TVERSION = 100, /* request for version handshake */ 77 | P9PROTO_RVERSION, /* response for version handshake */ 78 | P9PROTO_TAUTH = 102, /* request to establish authentication channel */ 79 | P9PROTO_RAUTH, /* response with authentication information */ 80 | P9PROTO_TATTACH = 104, /* establish a user access to a file system*/ 81 | P9PROTO_RATTACH, /* response with top level handle to file hierarchy */ 82 | P9PROTO_TERROR = 106, /* not used */ 83 | P9PROTO_RERROR, /* response for any failed request */ 84 | P9PROTO_TFLUSH = 108, /* request to abort a previous request */ 85 | P9PROTO_RFLUSH, /* response when previous request has been cancelled */ 86 | P9PROTO_TWALK = 110, /* descend a directory hierarchy */ 87 | P9PROTO_RWALK, /* response with new handle for position within hierarchy */ 88 | P9PROTO_TOPEN = 112, /* prepare file handle for I/O for an existing file */ 89 | P9PROTO_ROPEN, /* response with file access information */ 90 | P9PROTO_TCREATE = 114, /* prepare for handle for I/O on a new file */ 91 | P9PROTO_RCREATE, /* response with file access information */ 92 | P9PROTO_TREAD = 116, /* request to transfer data from a file */ 93 | P9PROTO_RREAD, /* response with data requested */ 94 | P9PROTO_TWRITE = 118, /* request to transfer data to a file */ 95 | P9PROTO_RWRITE, /* response with how much data was written to the file */ 96 | P9PROTO_TCLUNK = 120, /* forget about a handle to a file within the File System */ 97 | P9PROTO_RCLUNK, /* response from the server for forgetting the file handle */ 98 | P9PROTO_TREMOVE = 122, /* request to remove a file */ 99 | P9PROTO_RREMOVE, /* response when server has removed the file */ 100 | P9PROTO_TSTAT = 124, /* request file entity attributes */ 101 | P9PROTO_RSTAT, /* response with file entity attributes */ 102 | P9PROTO_TWSTAT = 126, /* request to update file entity attributes */ 103 | P9PROTO_RWSTAT, /* response when file entity attributes are updated */ 104 | }; 105 | 106 | /* File Open Modes */ 107 | enum p9_open_mode_t { 108 | P9PROTO_OREAD = 0x00, /* open file for reading only */ 109 | P9PROTO_OWRITE = 0x01, /* open file for writing only */ 110 | P9PROTO_ORDWR = 0x02, /* open file for both reading and writing */ 111 | P9PROTO_OEXEC = 0x03, /* open file for execution */ 112 | P9PROTO_OTRUNC = 0x10, /* truncate file to zero length before opening it */ 113 | P9PROTO_OREXEC = 0x20, /* close the file when exec system call is made */ 114 | P9PROTO_ORCLOSE = 0x40, /* remove the file when it is closed */ 115 | P9PROTO_OAPPEND = 0x80, /* open the file and seek to the end of the file */ 116 | P9PROTO_OEXCL = 0x1000, /* only create a file and not open it */ 117 | }; 118 | 119 | /* FIle Permissions */ 120 | enum p9_perm_t { 121 | P9PROTO_DMDIR = 0x80000000, /* permission bit for directories */ 122 | P9PROTO_DMAPPEND = 0x40000000, /* permission bit for is append-only */ 123 | P9PROTO_DMEXCL = 0x20000000, /* permission bit for exclusive use (only one open handle allowed) */ 124 | P9PROTO_DMMOUNT = 0x10000000, /* permission bit for mount points */ 125 | P9PROTO_DMAUTH = 0x08000000, /* permission bit for authentication file */ 126 | P9PROTO_DMTMP = 0x04000000, /* permission bit for non-backed-up files */ 127 | P9PROTO_DMSYMLINK = 0x02000000, /* permission bit for symbolic link (9P2000.u) */ 128 | P9PROTO_DMLINK = 0x01000000, /* permission bit for hard-link (9P2000.u) */ 129 | P9PROTO_DMDEVICE = 0x00800000, /* permission bit for device files (9P2000.u) */ 130 | P9PROTO_DMNAMEDPIPE = 0x00200000,/* permission bit for named pipe (9P2000.u) */ 131 | P9PROTO_DMSOCKET = 0x00100000, /* permission bit for socket (9P2000.u) */ 132 | P9PROTO_DMSETUID = 0x00080000, /* permission bit for setuid (9P2000.u) */ 133 | P9PROTO_DMSETGID = 0x00040000, /* permission bit for setgid (9P2000.u) */ 134 | P9PROTO_DMSETVTX = 0x00010000, /* permission bit for sticky bit (9P2000.u) */ 135 | }; 136 | 137 | /* 138 | * QID types - they are primarly used to 139 | * differentiate semantics for a file system 140 | */ 141 | enum p9_qid_t { 142 | P9PROTO_QTDIR = 0x80, /* directory */ 143 | P9PROTO_QTAPPEND = 0x40, /* append-only */ 144 | P9PROTO_QTEXCL = 0x20, /* exclusive use (only one open handle allowed)*/ 145 | P9PROTO_QTMOUNT = 0x10, /* mount points */ 146 | P9PROTO_QTAUTH = 0x08, /* authentication file */ 147 | P9PROTO_QTTMP = 0x04, /* non-backed-up files */ 148 | P9PROTO_QTSYMLINK = 0x02, /* symbolic links */ 149 | P9PROTO_QTLINK = 0x01, /* hard link */ 150 | P9PROTO_QTFILE = 0x00, /* normal files */ 151 | }; 152 | 153 | /* P9 Magic Numbers */ 154 | #define P9PROTO_NOFID (uint32_t)(~0) 155 | #define P9_DEFUNAME "nobody" 156 | #define P9_DEFANAME "" 157 | #define P9_NONUNAME (uint32_t)(~0) 158 | #define P9_MAXWELEM 16 159 | 160 | /* Exchange unit between Qemu and Client */ 161 | struct p9_qid { 162 | uint8_t type; /* the type of the file */ 163 | uint32_t version; /* version number for given path */ 164 | uint64_t path; /* the file servers unique id for file */ 165 | }; 166 | 167 | /* FS information stat structure */ 168 | struct p9_statfs { 169 | uint32_t type; /* type of file system */ 170 | uint32_t bsize; /* optimal transfer block size */ 171 | uint64_t blocks; /* total data blocks in file system */ 172 | uint64_t bfree; /* free blocks in fs */ 173 | uint64_t bavail; /* free blocks avail to non-superuser */ 174 | uint64_t files; /* total file nodes in file system */ 175 | uint64_t ffree; /* free file nodes in fs */ 176 | uint64_t fsid; /* file system id */ 177 | uint32_t namelen; /* maximum length of filenames */ 178 | }; 179 | 180 | 181 | /* File system metadata information */ 182 | struct p9_wstat { 183 | uint16_t size; /* total byte count of the following data */ 184 | uint16_t type; /* type of file */ 185 | uint32_t dev; /* id of device containing file */ 186 | struct p9_qid qid; /* identifier used by server for file system entity information */ 187 | uint32_t mode; /* protection */ 188 | uint32_t atime; /* time of last access */ 189 | uint32_t mtime; /* time of last modification */ 190 | uint64_t length; /* length of file in bytes */ 191 | char *name; /* file name */ 192 | char *uid; /* user ID of owner */ 193 | char *gid; /* group ID of owner */ 194 | char *muid; /* name of the user who last modified the file */ 195 | char *extension; /* 9p2000.u extensions */ 196 | uid_t n_uid; /* 9p2000.u extensions */ 197 | gid_t n_gid; /* 9p2000.u extensions */ 198 | uid_t n_muid; /* 9p2000.u extensions */ 199 | }; 200 | 201 | /* The linux version of FS information stat structure*/ 202 | struct p9_stat_dotl { 203 | uint64_t st_result_mask;/* indicates fields that are requested */ 204 | struct p9_qid qid; /* identifier used by server for file system entity information */ 205 | uint32_t st_mode; /* protection */ 206 | uid_t st_uid; /* user ID of owner */ 207 | gid_t st_gid; /* group ID of owner */ 208 | uint64_t st_nlink; /* number of hard links */ 209 | uint64_t st_rdev; /* device ID (if special file) */ 210 | uint64_t st_size; /* total size, in bytes */ 211 | uint64_t st_blksize; /* blocksize for file system I/O */ 212 | uint64_t st_blocks; /* number of 512B blocks allocated */ 213 | uint64_t st_atime_sec; /* time of last access, seconds */ 214 | uint64_t st_atime_nsec; /* time of last access, nanoseconds */ 215 | uint64_t st_mtime_sec; /* time of last modification, seconds */ 216 | uint64_t st_mtime_nsec; /* time of last modifictaion, nanoseconds */ 217 | uint64_t st_ctime_sec; /* time of last status change, seconds*/ 218 | uint64_t st_ctime_nsec; /* time of last status change, nanoseconds*/ 219 | uint64_t st_btime_sec; /* following memebers are reserved for future use */ 220 | uint64_t st_btime_nsec; 221 | uint64_t st_gen; 222 | uint64_t st_data_version; 223 | }; 224 | 225 | /* P9 inode attribute for setattr */ 226 | struct p9_iattr_dotl { 227 | uint32_t valid; /* bit fields specifying which fields are valid */ 228 | uint32_t mode; /* protection */ 229 | uid_t uid; /* user id of owner */ 230 | gid_t gid; /* group id */ 231 | uint64_t size; /* file size */ 232 | uint64_t atime_sec; /* last access time in seconds */ 233 | uint64_t atime_nsec; /* last access time in nanoseconds */ 234 | uint64_t mtime_sec; /* last modification time in seconds */ 235 | uint64_t mtime_nsec; /* last modification time in nanoseconds */ 236 | }; 237 | 238 | #define P9PROTO_STATS_MODE 0x00000001ULL 239 | #define P9PROTO_STATS_NLINK 0x00000002ULL 240 | #define P9PROTO_STATS_UID 0x00000004ULL 241 | #define P9PROTO_STATS_GID 0x00000008ULL 242 | #define P9PROTO_STATS_RDEV 0x00000010ULL 243 | #define P9PROTO_STATS_ATIME 0x00000020ULL 244 | #define P9PROTO_STATS_MTIME 0x00000040ULL 245 | #define P9PROTO_STATS_CTIME 0x00000080ULL 246 | #define P9PROTO_STATS_INO 0x00000100ULL 247 | #define P9PROTO_STATS_SIZE 0x00000200ULL 248 | #define P9PROTO_STATS_BLOCKS 0x00000400ULL 249 | 250 | #define P9PROTO_STATS_BTIME 0x00000800ULL 251 | #define P9PROTO_STATS_GEN 0x00001000ULL 252 | #define P9PROTO_STATS_DATA_VERSION 0x00002000ULL 253 | 254 | #define P9PROTO_STATS_BASIC 0x000007ffULL /* Mask for fields up to BLOCKS */ 255 | #define P9PROTO_STATS_ALL 0x00003fffULL /* Mask for All fields above */ 256 | 257 | #define P9PROTO_SETATTR_MODE 0x00000001UL 258 | #define P9PROTO_SETATTR_UID 0x00000002UL 259 | #define P9PROTO_SETATTR_GID 0x00000004UL 260 | #define P9PROTO_SETATTR_SIZE 0x00000008UL 261 | #define P9PROTO_SETATTR_ATIME 0x00000010UL 262 | #define P9PROTO_SETATTR_MTIME 0x00000020UL 263 | #define P9PROTO_SETATTR_CTIME 0x00000040UL 264 | #define P9PROTO_SETATTR_ATIME_SET 0x00000080UL 265 | #define P9PROTO_SETATTR_MTIME_SET 0x00000100UL 266 | #define P9PROTO_SETATTR_MASK 0x000001bfUL 267 | 268 | #define P9PROTO_TGETATTR_BLK 512 269 | 270 | /* PDU buffer used for SG lists. */ 271 | struct p9_buffer { 272 | uint32_t size; 273 | uint16_t tag; 274 | uint8_t id; 275 | size_t offset; 276 | size_t capacity; 277 | uint8_t *sdata; 278 | }; 279 | 280 | #endif /* VIRTIO_FS_9P_H */ 281 | -------------------------------------------------------------------------------- /sys/dev/virtio/virtio_fs_client.h: -------------------------------------------------------------------------------- 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 AND CONTRIBUTORS ``AS IS'' AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | * SUCH DAMAGE. 25 | */ 26 | 27 | /* 9P client definitions */ 28 | 29 | #ifndef VIRTIO_FS_CLIENT_H 30 | #define VIRTIO_FS_CLIENT_H 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | 48 | #include "virtio_fs_9p.h" 49 | 50 | /* 9P protocol versions */ 51 | enum p9_proto_versions { 52 | p9_proto_legacy, /* legacy version */ 53 | p9_proto_2000u, /* Unix version */ 54 | p9_proto_2000L, /* Linux version */ 55 | }; 56 | 57 | /* P9 Request exchanged between Host and Guest */ 58 | struct p9_req_t { 59 | struct p9_buffer *tc; /* request buffer */ 60 | struct p9_buffer *rc; /* response buffer */ 61 | }; 62 | 63 | /* 9P transport status */ 64 | enum transport_status { 65 | VIRTFS_CONNECT, /* transport is connected */ 66 | VIRTFS_BEGIN_DISCONNECT,/* transport has begun to disconnect */ 67 | VIRTFS_DISCONNECT, /* transport has been dosconnected */ 68 | }; 69 | 70 | /* This is set by QEMU so we will oblige */ 71 | #define VIRTFS_MTU 8192 72 | 73 | /* 74 | * Even though we have a 8k buffer, Qemu is typically doing 8168 75 | * because of a HDR of 24. Use that amount for transfers so that we dont 76 | * drop anything. 77 | */ 78 | #define VIRTFS_IOUNIT (VIRTFS_MTU - 24) 79 | #define VIRTFS_DIRENT_LEN 256 80 | #define P9_NOTAG 0 81 | 82 | /* Client state information */ 83 | struct p9_client { 84 | struct mtx p9clnt_mtx; /* mutex to lock the client */ 85 | struct mtx p9req_mtx; /* mutex to lock the request buffer */ 86 | struct cv req_cv; /* condition variable on which to wake up thread */ 87 | unsigned int msize; /* maximum data size */ 88 | unsigned char proto_version; /* 9P version to use */ 89 | struct p9_trans_module *trans_mod; /* module API instantiated with this client */ 90 | void *trans; /* tranport instance state and API */ 91 | struct unrhdr *fidpool; /* fid handle accounting for session */ 92 | struct unrhdr *tagpool; /* transaction id accounting for session */ 93 | enum transport_status trans_status; /* tranport instance state */ 94 | }; 95 | 96 | /* The main fid structure which keeps track of the file.*/ 97 | struct p9_fid { 98 | struct p9_client *clnt; /* the instatntiating 9P client */ 99 | uint32_t fid; /* numeric identifier */ 100 | int mode; /* current mode of this fid */ 101 | struct p9_qid qid; /* server identifier */ 102 | uint32_t mtu; /* max transferrable unit at a time */ 103 | uid_t uid; /* numeric uid of the local user who owns this handle */ 104 | int v_opens; /* keep count on the number of opens called with this fiel handle */ 105 | STAILQ_ENTRY(p9_fid) fid_next; /* points to next fid in the list */ 106 | }; 107 | 108 | /* Directory entry structure */ 109 | struct p9_dirent { 110 | struct p9_qid qid; /* 9P server qid for this dirent */ 111 | uint64_t d_off; /* offset to the next dirent */ 112 | unsigned char d_type; /* file type */ 113 | char d_name[VIRTFS_DIRENT_LEN]; /* file name */ 114 | int len; 115 | }; 116 | 117 | /* Session and client Init Ops */ 118 | struct p9_client *p9_client_create(struct mount *mp, int *error, 119 | const char *mount_tag); 120 | void p9_client_destroy(struct p9_client *clnt); 121 | struct p9_fid *p9_client_attach(struct p9_client *clnt, struct p9_fid *fid, 122 | const char *uname, uid_t n_uname, const char *aname, int *error); 123 | 124 | /* FILE OPS - These are individually called from the specific vop function */ 125 | 126 | int p9_client_open(struct p9_fid *fid, int mode); 127 | int p9_client_close(struct p9_fid *fid); 128 | struct p9_fid *p9_client_walk(struct p9_fid *oldfid, uint16_t nwnames, 129 | char **wnames, int clone, int *error); 130 | struct p9_fid *p9_fid_create(struct p9_client *clnt); 131 | void p9_fid_destroy(struct p9_fid *fid); 132 | uint16_t p9_tag_create(struct p9_client *clnt); 133 | void p9_tag_destroy(struct p9_client *clnt, uint16_t tag); 134 | int p9_client_clunk(struct p9_fid *fid); 135 | int p9_client_version(struct p9_client *clnt); 136 | int p9_client_readdir(struct p9_fid *fid, char *data, uint64_t offset, uint32_t count); 137 | int p9_client_read(struct p9_fid *fid, uint64_t offset, uint32_t count, char *data); 138 | int p9_client_write(struct p9_fid *fid, uint64_t offset, uint32_t count, char *data); 139 | int p9_client_file_create(struct p9_fid *fid, char *name, uint32_t perm, int mode, 140 | char *extension); 141 | int p9_client_remove(struct p9_fid *fid); 142 | int p9_dirent_read(struct p9_client *clnt, char *buf, int start, int len, 143 | struct p9_dirent *dirent); 144 | int p9_client_statfs(struct p9_fid *fid, struct p9_statfs *stat); 145 | int p9_client_statread(struct p9_client *clnt, char *data, size_t len, struct p9_wstat *st); 146 | int p9_is_proto_dotu(struct p9_client *clnt); 147 | int p9_is_proto_dotl(struct p9_client *clnt); 148 | void p9_client_cb(struct p9_client *c, struct p9_req_t *req); 149 | int p9stat_read(struct p9_client *clnt, char *data, size_t len, struct p9_wstat *st); 150 | void p9_client_disconnect(struct p9_client *clnt); 151 | void p9_client_begin_disconnect(struct p9_client *clnt); 152 | int p9_create_symlink(struct p9_fid *fid, char *name, char *symtgt, gid_t gid); 153 | int p9_create_hardlink(struct p9_fid *dfid, struct p9_fid *oldfid, char *name); 154 | int p9_readlink(struct p9_fid *fid, char **target); 155 | int p9_client_renameat(struct p9_fid *oldfid, char *oldname, struct p9_fid *newfid, char *newname); 156 | int p9_client_getattr(struct p9_fid *fid, struct p9_stat_dotl *stat_dotl, 157 | uint64_t request_mask); 158 | int p9_client_setattr(struct p9_fid *fid, struct p9_iattr_dotl *p9attr); 159 | 160 | extern int p9_debug_level; /* All debugs on now */ 161 | 162 | /* 9P debug flags */ 163 | #define P9_DEBUG_TRANS 0x0001 /* Trace transport */ 164 | #define P9_DEBUG_SUBR 0x0002 /* Trace driver submissions */ 165 | #define P9_DEBUG_VFS 0x0004 /* VFS API tracing */ 166 | #define P9_DEBUG_PROTO 0x0008 /* 9P protocol tracing */ 167 | #define P9_DEBUG_VOPS 0x0010 /* VOPs tracing */ 168 | #define P9_DEBUG_ERROR 0x0020 /* verbose error messages */ 169 | 170 | #define p9_debug(category, fmt, ...) do { \ 171 | if ((p9_debug_level & P9_DEBUG_##category) != 0) \ 172 | printf(fmt, ##__VA_ARGS__); \ 173 | } while (0) 174 | 175 | #endif /* VIRTIO_FS_CLIENT_H */ 176 | -------------------------------------------------------------------------------- /sys/dev/virtio/virtio_fs_protocol.h: -------------------------------------------------------------------------------- 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 AND CONTRIBUTORS ``AS IS'' AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | * SUCH DAMAGE. 25 | */ 26 | 27 | /* 9P protocol definitions */ 28 | 29 | #ifndef __VIRTIO_FS_PROTOCOL_H__ 30 | #define __VIRTIO_FS_PROTOCOL_H__ 31 | 32 | int p9_buf_vwritef(struct p9_buffer *buf, int proto_version, const char *fmt, 33 | va_list ap); 34 | int p9_buf_readf(struct p9_buffer *buf, int proto_version, const char *fmt, ...); 35 | int p9_buf_prepare(struct p9_buffer *buf, int8_t type); 36 | int p9_buf_finalize(struct p9_client *clnt, struct p9_buffer *buf); 37 | void p9_buf_reset(struct p9_buffer *buf); 38 | 39 | #endif /*__VIRTIO_FS_PROTOCOL_H__ */ 40 | -------------------------------------------------------------------------------- /sys/modules/virtio/9pfs/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2017 Juniper Networks, Inc. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 1. Redistributions of source code must retain the above copyright 8 | # notice, this list of conditions and the following disclaimer. 9 | # 2. Redistributions in binary form must reproduce the above copyright 10 | # notice, this list of conditions and the following disclaimer in the 11 | # documentation and/or other materials provided with the distribution. 12 | # 13 | # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 | # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 | # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 | # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 | # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 | # SUCH DAMAGE. 24 | # 25 | 26 | .include 27 | .PATH: ${.CURDIR}/../../../dev/virtio/9pfs 28 | 29 | KMOD = virtio_9pfs 30 | 31 | # Enumerate Source files for kernel module 32 | SRCS+= virtfs_subr.c 33 | SRCS+= virtfs_vfops.c 34 | SRCS+= virtfs_vnops.c 35 | SRCS+= vnode_if.h 36 | 37 | # Include kernel module makefile 38 | .include 39 | -------------------------------------------------------------------------------- /sys/modules/virtio/9pnet/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2017 Juniper Networks, Inc. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 1. Redistributions of source code must retain the above copyright 8 | # notice, this list of conditions and the following disclaimer. 9 | # 2. Redistributions in binary form must reproduce the above copyright 10 | # notice, this list of conditions and the following disclaimer in the 11 | # documentation and/or other materials provided with the distribution. 12 | # 13 | # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 | # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 | # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 | # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 | # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 | # SUCH DAMAGE. 24 | # 25 | 26 | .include 27 | .PATH: ${.CURDIR}/../../../dev/virtio/9pnet 28 | 29 | KMOD = virtio_9pnet 30 | 31 | # Enumerate Source files for kernel module 32 | SRCS = device_if.h bus_if.h opt_cam.h trans_virtio.c client.c protocol.c 33 | 34 | # Include kernel module makefile 35 | .include 36 | --------------------------------------------------------------------------------