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