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