1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 28 /* All Rights Reserved */ 29 30 /* 31 * University Copyright- Copyright (c) 1982, 1986, 1988 32 * The Regents of the University of California 33 * All Rights Reserved 34 * 35 * University Acknowledgment- Portions of this document are derived from 36 * software developed by the University of California, Berkeley, and its 37 * contributors. 38 */ 39 40 /* 41 * nfs umount 42 */ 43 44 #include <stdio.h> 45 #include <stdlib.h> 46 #include <string.h> 47 #include <stdarg.h> 48 #include <signal.h> 49 #include <unistd.h> 50 #include <kstat.h> 51 #include <rpc/rpc.h> 52 #include <sys/mnttab.h> 53 #include <sys/mount.h> 54 #include <sys/mntent.h> 55 #include <nfs/nfs.h> 56 #include <nfs/nfs_clnt.h> 57 #include <rpcsvc/mount.h> 58 #include <errno.h> 59 #include <locale.h> 60 #include <fslib.h> 61 #include <priv.h> 62 #include <tsol/label.h> 63 #include "replica.h" 64 65 #define RET_OK 0 66 #define RET_ERR 32 67 68 #ifdef __STDC__ 69 static void pr_err(const char *fmt, ...); 70 #else 71 static void pr_err(char *fmt, va_dcl); 72 #endif 73 static void usage(); 74 static int nfs_unmount(char *, int); 75 static void inform_server(char *, char *, bool_t); 76 static struct extmnttab *mnttab_find(); 77 extern int __clnt_bindresvport(CLIENT *); 78 79 static int is_v4_mount(struct extmnttab *); 80 81 static char *myname; 82 static char typename[64]; 83 84 int 85 main(int argc, char *argv[]) 86 { 87 extern int optind; 88 int c; 89 int umnt_flag = 0; 90 91 (void) setlocale(LC_ALL, ""); 92 93 #if !defined(TEXT_DOMAIN) 94 #define TEXT_DOMAIN "SYS_TEST" 95 #endif 96 (void) textdomain(TEXT_DOMAIN); 97 98 myname = strrchr(argv[0], '/'); 99 myname = myname ? myname+1 : argv[0]; 100 (void) snprintf(typename, sizeof (typename), "nfs %s", myname); 101 argv[0] = typename; 102 103 /* 104 * Set options 105 */ 106 while ((c = getopt(argc, argv, "f")) != EOF) { 107 switch (c) { 108 case 'f': 109 umnt_flag |= MS_FORCE; /* forced unmount is desired */ 110 break; 111 default: 112 usage(); 113 exit(RET_ERR); 114 } 115 } 116 if (argc - optind != 1) { 117 usage(); 118 exit(RET_ERR); 119 } 120 121 if (!priv_ineffect(PRIV_SYS_MOUNT) || 122 !priv_ineffect(PRIV_NET_PRIVADDR)) { 123 pr_err(gettext("insufficient privileges\n")); 124 exit(RET_ERR); 125 } 126 127 /* 128 * On a labeled system, a MAC flag may be needed if the mount is 129 * read-down, so attempt to assert it but ignore errors in any case. 130 */ 131 if (is_system_labeled()) 132 (void) setpflags(NET_MAC_AWARE, 1); 133 134 /* 135 * exit, really 136 */ 137 return (nfs_unmount(argv[optind], umnt_flag)); 138 } 139 140 static void 141 pr_err(const char *fmt, ...) 142 { 143 va_list ap; 144 145 va_start(ap, fmt); 146 (void) fprintf(stderr, "%s: ", typename); 147 (void) vfprintf(stderr, fmt, ap); 148 (void) fflush(stderr); 149 va_end(ap); 150 } 151 152 static void 153 usage() 154 { 155 (void) fprintf(stderr, 156 gettext("Usage: nfs umount [-f] {server:path | dir}\n")); 157 exit(RET_ERR); 158 } 159 160 static int 161 nfs_unmount(char *pathname, int umnt_flag) 162 { 163 struct extmnttab *mntp; 164 bool_t quick = FALSE; 165 int is_v4 = FALSE; 166 167 mntp = mnttab_find(pathname); 168 if (mntp) { 169 pathname = mntp->mnt_mountp; 170 } 171 172 if (mntp) 173 is_v4 = is_v4_mount(mntp); 174 175 /* Forced unmount will almost always be successful */ 176 if (umount2(pathname, umnt_flag) < 0) { 177 if (errno == EBUSY) { 178 pr_err(gettext("%s: is busy\n"), pathname); 179 } else { 180 pr_err(gettext("%s: not mounted\n"), pathname); 181 } 182 return (RET_ERR); 183 } 184 185 /* All done if it's v4 */ 186 if (is_v4 == TRUE) 187 return (RET_OK); 188 189 /* Inform server quickly in case of forced unmount */ 190 if (umnt_flag & MS_FORCE) 191 quick = TRUE; 192 193 if (mntp) { 194 inform_server(mntp->mnt_special, mntp->mnt_mntopts, quick); 195 } 196 197 return (RET_OK); 198 } 199 200 /* 201 * Find the mnttab entry that corresponds to "name". 202 * We're not sure what the name represents: either 203 * a mountpoint name, or a special name (server:/path). 204 * Return the last entry in the file that matches. 205 */ 206 static struct extmnttab * 207 mnttab_find(dirname) 208 char *dirname; 209 { 210 FILE *fp; 211 struct extmnttab mnt; 212 struct extmnttab *res = NULL; 213 214 fp = fopen(MNTTAB, "r"); 215 if (fp == NULL) { 216 pr_err("%s: %s\n", MNTTAB, strerror(errno)); 217 return (NULL); 218 } 219 while (getextmntent(fp, &mnt, sizeof (struct extmnttab)) == 0) { 220 if (strcmp(mnt.mnt_mountp, dirname) == 0 || 221 strcmp(mnt.mnt_special, dirname) == 0) { 222 if (res) 223 fsfreemnttab(res); 224 res = fsdupmnttab(&mnt); 225 } 226 } 227 228 (void) fclose(fp); 229 return (res); 230 } 231 232 /* 233 * If quick is TRUE, it will try to inform server quickly 234 * as possible. 235 */ 236 static void 237 inform_server(char *string, char *opts, bool_t quick) 238 { 239 struct timeval timeout; 240 CLIENT *cl; 241 enum clnt_stat rpc_stat; 242 struct replica *list; 243 int i, n; 244 char *p = NULL; 245 static struct timeval create_timeout = {5, 0}; 246 static struct timeval *timep; 247 248 list = parse_replica(string, &n); 249 250 if (list == NULL) { 251 if (n < 0) 252 pr_err(gettext("%s is not hostname:path format\n"), 253 string); 254 else 255 pr_err(gettext("no memory\n")); 256 return; 257 } 258 259 /* 260 * If mounted with -o public, then no need to contact server 261 * because mount protocol was not used. 262 */ 263 if (opts != NULL) 264 p = strstr(opts, MNTOPT_PUBLIC); 265 266 if (p != NULL) { 267 i = strlen(MNTOPT_PUBLIC); 268 /* 269 * Now make sure the match of "public" isn't a substring 270 * of another option. 271 */ 272 if (((p == opts) || (*(p-1) == ',')) && 273 ((p[i] == ',') || (p[i] == '\0'))) 274 return; 275 } 276 277 for (i = 0; i < n; i++) { 278 int vers; 279 280 /* 281 * Skip file systems mounted using WebNFS, because mount 282 * protocol was not used. 283 */ 284 if (strcmp(list[i].host, "nfs") == 0 && strncmp(list[i].path, 285 "//", 2) == 0) 286 continue; 287 288 vers = MOUNTVERS; 289 retry: 290 /* 291 * Use 5 sec. timeout if file system is forced unmounted, 292 * otherwise use default timeout to create a client handle. 293 * This would minimize the time to force unmount a file 294 * system reside on a server that is down. 295 */ 296 timep = (quick ? &create_timeout : NULL); 297 cl = clnt_create_timed(list[i].host, MOUNTPROG, vers, 298 "datagram_n", timep); 299 /* 300 * Do not print any error messages in case of forced 301 * unmount. 302 */ 303 if (cl == NULL) { 304 if (!quick) 305 pr_err("%s:%s %s\n", list[i].host, list[i].path, 306 clnt_spcreateerror( 307 "server not responding")); 308 continue; 309 } 310 /* 311 * Now it is most likely that the NFS client will be able 312 * to contact the server since rpcbind is running on 313 * the server. There is still a small window in which 314 * server can be unreachable. 315 */ 316 317 if (__clnt_bindresvport(cl) < 0) { 318 if (!quick) 319 pr_err(gettext( 320 "couldn't bind to reserved port\n")); 321 clnt_destroy(cl); 322 return; 323 } 324 if ((cl->cl_auth = authsys_create_default()) == NULL) { 325 if (!quick) 326 pr_err(gettext( 327 "couldn't create authsys structure\n")); 328 clnt_destroy(cl); 329 return; 330 } 331 timeout.tv_usec = 0; 332 timeout.tv_sec = 5; 333 clnt_control(cl, CLSET_RETRY_TIMEOUT, (char *)&timeout); 334 timeout.tv_sec = 25; 335 rpc_stat = clnt_call(cl, MOUNTPROC_UMNT, xdr_dirpath, 336 (char *)&list[i].path, xdr_void, (char *)NULL, 337 timeout); 338 AUTH_DESTROY(cl->cl_auth); 339 clnt_destroy(cl); 340 if (rpc_stat == RPC_PROGVERSMISMATCH && vers == MOUNTVERS) { 341 /* 342 * The rare case of a v3-only server 343 */ 344 vers = MOUNTVERS3; 345 goto retry; 346 } 347 if (rpc_stat != RPC_SUCCESS) 348 pr_err("%s\n", clnt_sperror(cl, "unmount")); 349 } 350 351 free_replica(list, n); 352 } 353 354 /* 355 * This function's behavior is taken from nfsstat. 356 * Trying to determine what NFS version was used for the mount. 357 */ 358 int 359 is_v4_mount(struct extmnttab *mntp) 360 { 361 kstat_ctl_t *kc = NULL; /* libkstat cookie */ 362 kstat_t *ksp; 363 struct mntinfo_kstat mik; 364 365 if (mntp == NULL) 366 return (FALSE); 367 368 if ((kc = kstat_open()) == NULL) 369 return (FALSE); 370 371 for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) { 372 if (ksp->ks_type != KSTAT_TYPE_RAW) 373 continue; 374 if (strcmp(ksp->ks_module, "nfs") != 0) 375 continue; 376 if (strcmp(ksp->ks_name, "mntinfo") != 0) 377 continue; 378 if (mntp->mnt_minor != ksp->ks_instance) 379 continue; 380 381 if (kstat_read(kc, ksp, &mik) == -1) 382 continue; 383 384 if (mik.mik_vers == 4) 385 return (TRUE); 386 else 387 return (FALSE); 388 } 389 return (FALSE); 390 } 391