1 /* $NetBSD: ninepuffs.c,v 1.33 2020/06/14 00:30:20 uwe Exp $ */ 2 3 /* 4 * Copyright (c) 2007 Antti Kantee. All Rights Reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 16 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28 /* 29 * 9puffs: access a 9P file server as a vfs via puffs 30 */ 31 32 #include <sys/cdefs.h> 33 #ifndef lint 34 __RCSID("$NetBSD: ninepuffs.c,v 1.33 2020/06/14 00:30:20 uwe Exp $"); 35 #endif /* !lint */ 36 37 #include <sys/types.h> 38 #include <sys/socket.h> 39 #include <sys/poll.h> 40 41 #include <netinet/in.h> 42 43 #include <assert.h> 44 #include <err.h> 45 #include <errno.h> 46 #include <netdb.h> 47 #include <pwd.h> 48 #include <puffs.h> 49 #include <stdio.h> 50 #include <stdlib.h> 51 #include <unistd.h> 52 53 #include "ninepuffs.h" 54 #include "nineproto.h" 55 56 #define DEFPORT_9P "564" /* "9pfs", but don't depend on it being in services */ 57 58 __dead static void 59 usage(void) 60 { 61 62 fprintf(stderr, "usage: %s [-46su] [-o mntopts] [-p port] " 63 "[user@]server[:path] mountpoint\n", getprogname()); 64 fprintf(stderr, " %s -c [-su] [-o mntopts] device mountpoint\n", 65 getprogname()); 66 exit(1); 67 } 68 69 /* 70 * TCP connection to 9P file server. 71 * Return connected socket or exit with error. 72 */ 73 static int 74 serverconnect(const char *hostname, const char *portname, int family) 75 { 76 int ret; 77 78 struct addrinfo hints; 79 memset(&hints, 0, sizeof(hints)); 80 hints.ai_family = family; 81 hints.ai_socktype = SOCK_STREAM; 82 83 if (portname == NULL) { 84 portname = DEFPORT_9P; 85 hints.ai_flags |= AI_NUMERICSERV; 86 } 87 88 struct addrinfo *ai0; 89 ret = getaddrinfo(hostname, portname, &hints, &ai0); 90 if (ret != 0) 91 errx(EXIT_FAILURE, "%s", gai_strerror(ret)); 92 93 int s = -1; 94 const char *cause = NULL; 95 for (struct addrinfo *ai = ai0; ai != NULL; ai = ai->ai_next) { 96 s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); 97 if (s < 0) { 98 cause = "socket"; 99 continue; 100 } 101 102 const int opt = 1; 103 ret = setsockopt(s, SOL_SOCKET, SO_NOSIGPIPE, &opt, sizeof(opt)); 104 if (ret < 0) { 105 cause = "SO_NOSIGPIPE"; 106 continue; 107 } 108 109 ret = connect(s, ai->ai_addr, ai->ai_addrlen); 110 if (ret < 0) { 111 close(s); 112 s = -1; 113 cause = "connect"; 114 continue; 115 } 116 } 117 118 if (s < 0) 119 err(EXIT_FAILURE, "%s", cause); 120 121 freeaddrinfo(ai0); 122 return s; 123 } 124 125 static int 126 open_cdev(const char *path) 127 { 128 int s; 129 130 s = open(path, O_RDWR, 0); 131 if (s == -1) 132 err(1, "%s", path); 133 return s; 134 } 135 136 int 137 main(int argc, char *argv[]) 138 { 139 struct puffs9p p9p; 140 struct puffs_usermount *pu; 141 struct puffs_ops *pops; 142 struct puffs_node *pn_root; 143 mntoptparse_t mp; 144 int family; 145 const char *user, *srvhost, *srvpath; 146 char *p; 147 const char *port; 148 int mntflags, pflags, ch; 149 int detach; 150 int protover; 151 int server; 152 153 setprogname(argv[0]); 154 155 if (argc < 2) 156 usage(); 157 158 mntflags = pflags = 0; 159 detach = 1; 160 #ifdef INET6 161 family = AF_UNSPEC; 162 #else 163 family = AF_INET; 164 #endif 165 port = NULL; 166 protover = P9PROTO_VERSION; 167 server = P9P_SERVER_TCP; 168 169 while ((ch = getopt(argc, argv, "46co:p:su")) != -1) { 170 switch (ch) { 171 case '4': 172 family = AF_INET; 173 break; 174 case '6': 175 #ifdef INET6 176 family = AF_INET6; 177 break; 178 #else 179 errno = EPFNOSUPPORT; 180 err(EXIT_FAILURE, "IPv6"); 181 /* NOTREACHED */ 182 #endif 183 case 'c': 184 server = P9P_SERVER_CDEV; 185 break; 186 case 'o': 187 mp = getmntopts(optarg, puffsmopts, &mntflags, &pflags); 188 if (mp == NULL) 189 err(1, "getmntopts"); 190 freemntopts(mp); 191 break; 192 case 'p': 193 port = optarg; 194 break; 195 case 's': 196 detach = 0; 197 break; 198 case 'u': 199 protover = P9PROTO_VERSION_U; 200 break; 201 default: 202 usage(); 203 /*NOTREACHED*/ 204 } 205 } 206 argc -= optind; 207 argv += optind; 208 209 if (argc != 2) 210 usage(); 211 212 if (pflags & PUFFS_FLAG_OPDUMP) 213 detach = 0; 214 pflags |= PUFFS_KFLAG_WTCACHE | PUFFS_KFLAG_IAONDEMAND; 215 216 PUFFSOP_INIT(pops); 217 218 PUFFSOP_SET(pops, puffs9p, fs, unmount); 219 PUFFSOP_SETFSNOP(pops, sync); 220 PUFFSOP_SETFSNOP(pops, statvfs); 221 222 PUFFSOP_SET(pops, puffs9p, node, lookup); 223 PUFFSOP_SET(pops, puffs9p, node, readdir); 224 PUFFSOP_SET(pops, puffs9p, node, getattr); 225 PUFFSOP_SET(pops, puffs9p, node, setattr); 226 PUFFSOP_SET(pops, puffs9p, node, create); 227 PUFFSOP_SET(pops, puffs9p, node, open); 228 PUFFSOP_SET(pops, puffs9p, node, mkdir); 229 PUFFSOP_SET(pops, puffs9p, node, remove); 230 PUFFSOP_SET(pops, puffs9p, node, rmdir); 231 PUFFSOP_SET(pops, puffs9p, node, read); 232 PUFFSOP_SET(pops, puffs9p, node, write); 233 PUFFSOP_SET(pops, puffs9p, node, rename); 234 PUFFSOP_SET(pops, puffs9p, node, inactive); 235 PUFFSOP_SET(pops, puffs9p, node, reclaim); 236 #if 0 237 PUFFSOP_SET(pops, puffs9p, node, mknod); 238 #endif 239 240 pu = puffs_init(pops, argv[0], "9p", &p9p, pflags); 241 if (pu == NULL) 242 err(1, "puffs_init"); 243 244 memset(&p9p, 0, sizeof(p9p)); 245 p9p.maxreq = P9P_DEFREQLEN; 246 p9p.nextfid = 1; 247 p9p.protover = protover; 248 p9p.server = server; 249 250 /* user@ */ 251 if ((p = strchr(argv[0], '@')) != NULL) { 252 *p = '\0'; 253 srvhost = p+1; 254 user = argv[0]; 255 } else { 256 struct passwd *pw; 257 258 srvhost = argv[0]; 259 pw = getpwuid(getuid()); 260 if (pw == NULL) 261 err(1, "getpwuid"); 262 user = pw->pw_name; 263 } 264 265 /* [host] or [host]:/path with square brackets around host */ 266 if (server == P9P_SERVER_TCP && *srvhost == '[') { 267 ++srvhost; 268 if ((p = strchr(srvhost, ']')) == NULL) 269 errx(EXIT_FAILURE, "Missing bracket after the host name"); 270 *p++ = '\0'; 271 if (*p == '\0') /* [host] */ 272 srvpath = "/"; 273 else if (*p == ':') /* [host]:path */ 274 srvpath = p+1; 275 else /* [foo]bar */ 276 errx(EXIT_FAILURE, "Invalid brackets in the host name"); 277 278 } else { /* host or host:/path without brackets around host */ 279 if ((p = strchr(srvhost, ':')) != NULL) { 280 *p = '\0'; 281 srvpath = p+1; 282 } else { 283 srvpath = "/"; 284 } 285 } 286 287 if (*srvpath == '\0') 288 errx(1, "Empty path"); 289 if (*srvpath != '/') 290 errx(1, "%s: Not an absolute path", srvpath); 291 292 293 if (p9p.server == P9P_SERVER_TCP) { 294 p9p.servsock = serverconnect(srvhost, port, family); 295 } else { 296 /* path to a vio9p(4) device, e.g., /dev/vio9p0 */ 297 p9p.servsock = open_cdev(argv[0]); 298 } 299 300 if ((pn_root = p9p_handshake(pu, user, srvpath)) == NULL) { 301 close(p9p.servsock); 302 puffs_exit(pu, 1); 303 exit(1); 304 } 305 306 puffs_framev_init(pu, p9pbuf_read, p9pbuf_write, p9pbuf_cmp, NULL, 307 puffs_framev_unmountonclose); 308 if (puffs_framev_addfd(pu, p9p.servsock, 309 PUFFS_FBIO_READ | PUFFS_FBIO_WRITE) == -1) 310 err(1, "puffs_framebuf_addfd"); 311 312 if (detach) 313 if (puffs_daemon(pu, 1, 1) == -1) 314 err(1, "puffs_daemon"); 315 316 if (puffs_mount(pu, argv[1], mntflags, pn_root) == -1) 317 err(1, "puffs_mount"); 318 if (puffs_setblockingmode(pu, PUFFSDEV_NONBLOCK) == -1) 319 err(1, "setblockingmode"); 320 321 if (puffs_mainloop(pu) == -1) 322 err(1, "mainloop"); 323 close(p9p.servsock); 324 puffs_exit(pu, 1); 325 326 return 0; 327 } 328