1 /* $NetBSD: ninepuffs.c,v 1.35 2025/03/10 21:21:20 riastradh 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.35 2025/03/10 21:21:20 riastradh 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 bool cachename = false; 153 154 setprogname(argv[0]); 155 156 if (argc < 2) 157 usage(); 158 159 mntflags = pflags = 0; 160 detach = 1; 161 #ifdef INET6 162 family = AF_UNSPEC; 163 #else 164 family = AF_INET; 165 #endif 166 port = NULL; 167 protover = P9PROTO_VERSION; 168 server = P9P_SERVER_TCP; 169 170 while ((ch = getopt(argc, argv, "46cCo:p:su")) != -1) { 171 switch (ch) { 172 case '4': 173 family = AF_INET; 174 break; 175 case '6': 176 #ifdef INET6 177 family = AF_INET6; 178 break; 179 #else 180 errno = EPFNOSUPPORT; 181 err(EXIT_FAILURE, "IPv6"); 182 /* NOTREACHED */ 183 #endif 184 case 'c': 185 server = P9P_SERVER_CDEV; 186 break; 187 case 'C': 188 cachename = true; 189 break; 190 case 'o': 191 mp = getmntopts(optarg, puffsmopts, &mntflags, &pflags); 192 if (mp == NULL) 193 err(1, "getmntopts"); 194 freemntopts(mp); 195 break; 196 case 'p': 197 port = optarg; 198 break; 199 case 's': 200 detach = 0; 201 break; 202 case 'u': 203 protover = P9PROTO_VERSION_U; 204 break; 205 default: 206 usage(); 207 /*NOTREACHED*/ 208 } 209 } 210 argc -= optind; 211 argv += optind; 212 213 if (argc != 2) 214 usage(); 215 216 if (pflags & PUFFS_FLAG_OPDUMP) 217 detach = 0; 218 pflags |= PUFFS_KFLAG_WTCACHE | PUFFS_KFLAG_IAONDEMAND; 219 220 if (!cachename) 221 pflags |= PUFFS_KFLAG_NOCACHE_NAME; 222 223 PUFFSOP_INIT(pops); 224 225 PUFFSOP_SET(pops, puffs9p, fs, unmount); 226 PUFFSOP_SETFSNOP(pops, sync); 227 PUFFSOP_SETFSNOP(pops, statvfs); 228 229 PUFFSOP_SET(pops, puffs9p, node, lookup); 230 PUFFSOP_SET(pops, puffs9p, node, readdir); 231 PUFFSOP_SET(pops, puffs9p, node, getattr); 232 PUFFSOP_SET(pops, puffs9p, node, setattr); 233 PUFFSOP_SET(pops, puffs9p, node, create); 234 PUFFSOP_SET(pops, puffs9p, node, open); 235 PUFFSOP_SET(pops, puffs9p, node, mkdir); 236 PUFFSOP_SET(pops, puffs9p, node, remove); 237 PUFFSOP_SET(pops, puffs9p, node, rmdir); 238 PUFFSOP_SET(pops, puffs9p, node, read); 239 PUFFSOP_SET(pops, puffs9p, node, write); 240 PUFFSOP_SET(pops, puffs9p, node, rename); 241 PUFFSOP_SET(pops, puffs9p, node, inactive); 242 PUFFSOP_SET(pops, puffs9p, node, reclaim); 243 PUFFSOP_SET(pops, puffs9p, node, pathconf); 244 #if 0 245 PUFFSOP_SET(pops, puffs9p, node, mknod); 246 #endif 247 248 pu = puffs_init(pops, argv[0], "9p", &p9p, pflags); 249 if (pu == NULL) 250 err(1, "puffs_init"); 251 252 memset(&p9p, 0, sizeof(p9p)); 253 p9p.maxreq = P9P_DEFREQLEN; 254 p9p.nextfid = 1; 255 p9p.protover = protover; 256 p9p.server = server; 257 258 /* user@ */ 259 if ((p = strchr(argv[0], '@')) != NULL) { 260 *p = '\0'; 261 srvhost = p+1; 262 user = argv[0]; 263 } else { 264 struct passwd *pw; 265 266 srvhost = argv[0]; 267 pw = getpwuid(getuid()); 268 if (pw == NULL) 269 err(1, "getpwuid"); 270 user = pw->pw_name; 271 } 272 273 /* [host] or [host]:/path with square brackets around host */ 274 if (server == P9P_SERVER_TCP && *srvhost == '[') { 275 ++srvhost; 276 if ((p = strchr(srvhost, ']')) == NULL) 277 errx(EXIT_FAILURE, "Missing bracket after the host name"); 278 *p++ = '\0'; 279 if (*p == '\0') /* [host] */ 280 srvpath = "/"; 281 else if (*p == ':') /* [host]:path */ 282 srvpath = p+1; 283 else /* [foo]bar */ 284 errx(EXIT_FAILURE, "Invalid brackets in the host name"); 285 286 } else { /* host or host:/path without brackets around host */ 287 if ((p = strchr(srvhost, ':')) != NULL) { 288 *p = '\0'; 289 srvpath = p+1; 290 } else { 291 srvpath = "/"; 292 } 293 } 294 295 if (*srvpath == '\0') 296 errx(1, "Empty path"); 297 if (*srvpath != '/') 298 errx(1, "%s: Not an absolute path", srvpath); 299 300 301 if (p9p.server == P9P_SERVER_TCP) { 302 p9p.servsock = serverconnect(srvhost, port, family); 303 } else { 304 /* path to a vio9p(4) device, e.g., /dev/vio9p0 */ 305 p9p.servsock = open_cdev(argv[0]); 306 } 307 308 if ((pn_root = p9p_handshake(pu, user, srvpath)) == NULL) { 309 close(p9p.servsock); 310 puffs_exit(pu, 1); 311 exit(1); 312 } 313 314 puffs_framev_init(pu, p9pbuf_read, p9pbuf_write, p9pbuf_cmp, NULL, 315 puffs_framev_unmountonclose); 316 if (puffs_framev_addfd(pu, p9p.servsock, 317 PUFFS_FBIO_READ | PUFFS_FBIO_WRITE) == -1) 318 err(1, "puffs_framebuf_addfd"); 319 320 if (detach) 321 if (puffs_daemon(pu, 1, 1) == -1) 322 err(1, "puffs_daemon"); 323 324 if (puffs_mount(pu, argv[1], mntflags, pn_root) == -1) 325 err(1, "puffs_mount"); 326 if (puffs_setblockingmode(pu, PUFFSDEV_NONBLOCK) == -1) 327 err(1, "setblockingmode"); 328 329 if (puffs_mainloop(pu) == -1) 330 err(1, "mainloop"); 331 close(p9p.servsock); 332 puffs_exit(pu, 1); 333 334 return 0; 335 } 336