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