1 /* 2 * Copyright (c) 2011-2015 The DragonFly Project. All rights reserved. 3 * 4 * This code is derived from software contributed to The DragonFly Project 5 * by Matthew Dillon <dillon@dragonflybsd.org> 6 * by Venkatesh Srinivas <vsrinivas@dragonflybsd.org> 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in 16 * the documentation and/or other materials provided with the 17 * distribution. 18 * 3. Neither the name of The DragonFly Project nor the names of its 19 * contributors may be used to endorse or promote products derived 20 * from this software without specific, prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 23 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 24 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 25 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 26 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 27 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 28 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 29 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 30 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 31 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 32 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 #include <sys/types.h> 36 #include <sys/param.h> 37 #include <sys/mount.h> 38 #include <sys/socket.h> 39 #include <netinet/in.h> 40 #include <vfs/hammer2/hammer2_mount.h> 41 42 #include <stdio.h> 43 #include <stdlib.h> 44 #include <stdarg.h> 45 #include <string.h> 46 #include <unistd.h> 47 #include <errno.h> 48 #include <dmsg.h> 49 #include <mntopts.h> 50 51 static int cluster_connect(const char *volume); 52 static void usage(const char *ctl, ...); 53 54 static struct mntopt mopts[] = { 55 MOPT_STDOPTS, 56 MOPT_UPDATE, 57 { "local", 0, HMNT2_LOCAL, 1 }, 58 MOPT_NULL 59 }; 60 61 /* 62 * Usage: mount_hammer2 [volume] [mtpt] 63 */ 64 int 65 main(int ac, char *av[]) 66 { 67 struct hammer2_mount_info info; 68 struct vfsconf vfc; 69 char *mountpt; 70 char *devpath; 71 int error; 72 int ch; 73 int mount_flags; 74 int init_flags; 75 76 bzero(&info, sizeof(info)); 77 mount_flags = 0; 78 init_flags = 0; 79 80 while ((ch = getopt(ac, av, "o:u")) != -1) { 81 switch(ch) { 82 case 'o': 83 getmntopts(optarg, mopts, &mount_flags, &info.hflags); 84 break; 85 case 'u': 86 init_flags |= MNT_UPDATE; 87 break; 88 default: 89 usage("unknown option: -%c", ch); 90 /* not reached */ 91 } 92 } 93 ac -= optind; 94 av += optind; 95 mount_flags |= init_flags; 96 97 error = getvfsbyname("hammer2", &vfc); 98 if (error) { 99 fprintf(stderr, "hammer2 vfs not loaded\n"); 100 exit(1); 101 } 102 103 /* 104 * Only the mount point need be specified in update mode. 105 */ 106 if (init_flags & MNT_UPDATE) { 107 if (ac != 1) { 108 usage("missing parameter (node)"); 109 /* not reached */ 110 } 111 mountpt = av[0]; 112 if (mount(vfc.vfc_name, mountpt, mount_flags, &info)) 113 usage("mount %s: %s", mountpt, strerror(errno)); 114 exit(0); 115 } 116 117 /* 118 * New mount 119 */ 120 if (ac != 2) { 121 usage("missing parameter(s) (special[@label] node)"); 122 /* not reached */ 123 } 124 125 devpath = strdup(av[0]); 126 mountpt = av[1]; 127 128 if (devpath[0] == 0) { 129 fprintf(stderr, "mount_hammer2: empty device path\n"); 130 exit(1); 131 } 132 133 /* 134 * Automatically add @BOOT, @ROOT, or @DATA if no label specified, 135 * based on the slice. 136 */ 137 if (strchr(devpath, '@') == NULL) { 138 char slice = devpath[strlen(devpath)-1]; 139 switch(slice) { 140 case 'a': 141 asprintf(&devpath, "%s@BOOT", devpath); 142 break; 143 case 'd': 144 asprintf(&devpath, "%s@ROOT", devpath); 145 break; 146 default: 147 asprintf(&devpath, "%s@DATA", devpath); 148 break; 149 } 150 } 151 152 /* 153 * Connect to the cluster controller. This handles both remote 154 * mounts and device cache/master/slave mounts. 155 * 156 * When doing remote mounts that are allowed to run in the background 157 * the mount program will fork, detach, print a message, and exit(0) 158 * the originator while retrying in the background. 159 * 160 * Don't exit on failure, this isn't likely going to work for 161 * the root [re]mount in early boot. 162 */ 163 info.cluster_fd = cluster_connect(devpath); 164 if (info.cluster_fd < 0) { 165 fprintf(stderr, 166 "mount_hammer2: cluster_connect(%s) failed\n", 167 devpath); 168 } 169 170 /* 171 * Try to mount it, prefix if necessary. 172 */ 173 if (!strchr(devpath, ':') && devpath[0] != '/' && devpath[0] != '@') { 174 char *p2; 175 asprintf(&p2, "/dev/%s", devpath); 176 free(devpath); 177 devpath = p2; 178 } 179 info.volume = devpath; 180 181 error = mount(vfc.vfc_name, mountpt, mount_flags, &info); 182 if (error < 0) { 183 if (errno == ERANGE) { 184 fprintf(stderr, 185 "mount_hammer2: %s integrated with %s\n", 186 info.volume, mountpt); 187 } else if (errno == ENOENT) { 188 fprintf(stderr, "mount_hammer2: %s not found\n", 189 info.volume); 190 } else if (errno == ENXIO) { 191 fprintf(stderr, "mount_hammer2: incorrect volume " 192 "specification %s\n", 193 info.volume); 194 } else { 195 perror("mount"); 196 } 197 exit(1); 198 } 199 free(devpath); 200 201 /* 202 * XXX fork a backgrounded reconnector process to handle connection 203 * failures. 204 */ 205 206 return (0); 207 } 208 209 /* 210 * Connect to the cluster controller. We can connect to a local or remote 211 * cluster controller, depending. For a multi-node cluster we always want 212 * to connect to the local controller and let it maintain the connections 213 * to the multiple remote nodes. 214 */ 215 static 216 int 217 cluster_connect(const char *volume __unused) 218 { 219 struct sockaddr_in lsin; 220 int fd; 221 222 /* 223 * This starts the hammer2 service if it isn't already running, 224 * so we can connect to it. 225 */ 226 system("/sbin/hammer2 -q service"); 227 228 /* 229 * Connect us to the service but leave the rest to the kernel. 230 * If the connection is lost during the mount 231 */ 232 if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { 233 perror("socket"); 234 return(-1); 235 } 236 bzero(&lsin, sizeof(lsin)); 237 lsin.sin_family = AF_INET; 238 lsin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 239 lsin.sin_port = htons(DMSG_LISTEN_PORT); 240 241 if (connect(fd, (struct sockaddr *)&lsin, sizeof(lsin)) < 0) { 242 close(fd); 243 fprintf(stderr, "mount_hammer2: unable to connect to " 244 "cluster controller\n"); 245 return(-1); 246 } 247 248 return(fd); 249 } 250 251 static 252 void 253 usage(const char *ctl, ...) 254 { 255 va_list va; 256 257 va_start(va, ctl); 258 fprintf(stderr, "mount_hammer2: "); 259 vfprintf(stderr, ctl, va); 260 va_end(va); 261 fprintf(stderr, "\n"); 262 fprintf(stderr, " mount_hammer2 [-o options] special[@label] node\n"); 263 fprintf(stderr, " mount_hammer2 [-o options] @label node\n"); 264 fprintf(stderr, " mount_hammer2 -u [-o options] node\n"); 265 fprintf(stderr, "\n"); 266 fprintf(stderr, "options:\n" 267 " <standard_mount_options>\n" 268 " local\t- disable PFS clustering for whole device\n" 269 ); 270 exit(1); 271 } 272