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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * ----------------------------------------------------------------- 24 * 25 * umount.c 26 * 27 * CFS specific umount command. 28 */ 29 30 #pragma ident "%Z%%M% %I% %E% SMI" 31 32 /* 33 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 34 * Use is subject to license terms. 35 */ 36 37 #include <locale.h> 38 #include <stdio.h> 39 #include <stdlib.h> 40 #include <string.h> 41 #include <stdarg.h> 42 #include <unistd.h> 43 #include <limits.h> 44 #include <errno.h> 45 #include <wait.h> 46 #include <ctype.h> 47 #include <fcntl.h> 48 #include <signal.h> 49 #include <sys/types.h> 50 #include <sys/param.h> 51 #include <sys/stat.h> 52 #include <sys/fcntl.h> 53 #include <sys/mount.h> 54 #include <sys/mntent.h> 55 #include <sys/mnttab.h> 56 #include <sys/fs/cachefs_fs.h> 57 #include <fslib.h> 58 #include <sys/utsname.h> 59 #include <rpc/rpc.h> 60 #include "../common/subr.h" 61 #include "../common/cachefsd.h" 62 63 /* forward references */ 64 void pr_err(char *fmt, ...); 65 void usage(char *msgp); 66 int daemon_unmount(char *mntptp, int); 67 68 /* 69 * 70 * main 71 * 72 * Description: 73 * Main routine for the cfs umount program. 74 * Arguments: 75 * argc number of command line arguments 76 * argv list of command line arguments 77 * Returns: 78 * Returns 0 for success or 1 if an error occurs. 79 * Preconditions: 80 */ 81 82 int 83 main(int argc, char **argv) 84 { 85 char *strp; 86 int xx; 87 int ret; 88 struct mnttab mget; 89 struct mnttab mref; 90 FILE *finp; 91 char mnt_front[PATH_MAX]; 92 char mnt_back[PATH_MAX]; 93 char *p, mnt_frontns[PATH_MAX]; 94 pid_t pid; 95 int stat_loc; 96 char *cachedirp, *s; 97 int ufd, c; 98 int flag = 0; 99 100 (void) setlocale(LC_ALL, ""); 101 #if !defined(TEXT_DOMAIN) 102 #define TEXT_DOMAIN "SYS_TEST" 103 #endif 104 (void) textdomain(TEXT_DOMAIN); 105 106 /* 107 * Set options 108 */ 109 while ((c = getopt(argc, argv, "f")) != EOF) { 110 switch (c) { 111 case 'f': 112 flag |= MS_FORCE; /* forced unmount is desired */ 113 break; 114 default: 115 usage("Incorrect number of arguments specified"); 116 return (1); 117 } 118 } 119 if (flag & MS_FORCE) 120 strcpy(mnt_front, argv[2]); 121 else 122 strcpy(mnt_front, argv[1]); 123 mnt_back[0] = '\0'; 124 125 if (strlen(argv[1]) >= (size_t)PATH_MAX) { 126 pr_err(gettext("name too long")); 127 return (1); 128 } 129 130 /* 131 * An unmount from autofs may have a 132 * space appended to the path. 133 * Trim it off before attempting to 134 * match the mountpoint in mnttab 135 */ 136 strcpy(mnt_frontns, mnt_front); 137 p = &mnt_frontns[strlen(mnt_frontns) - 1]; 138 if (*p == ' ') 139 *p = '\0'; 140 141 /* get the mount point and the back file system mount point */ 142 finp = fopen(MNTTAB, "r"); 143 if (finp) { 144 mntnull(&mref); 145 mref.mnt_mountp = mnt_frontns; 146 mref.mnt_fstype = "cachefs"; 147 ret = getmntany(finp, &mget, &mref); 148 149 cachedirp = (char *)malloc(strlen(mget.mnt_special) + 1); 150 strcpy(cachedirp, mget.mnt_special); 151 /* 152 * AutoClient mounts do not have .cfs_mnt_points string in 153 * them, so strstr would return NULL. So .cfs_unmnt file 154 * is not updated. 155 */ 156 if ((s = strstr(cachedirp, ".cfs_mnt_points")) != NULL) { 157 time32_t btime; 158 159 xx = -1; 160 cachedirp[s-cachedirp] = '\0'; 161 strcat(cachedirp, CACHEFS_UNMNT_FILE); 162 if ((ufd = open(cachedirp, O_WRONLY|O_CREAT|O_TRUNC, 163 0600)) > 0) { 164 if ((btime = get_boottime()) != -1) 165 xx = write(ufd, &btime, sizeof (time32_t)); 166 close(ufd); 167 } 168 if (xx < 0) 169 pr_err(gettext(".cfs_unmnt error")); 170 } 171 172 if (ret != -1) { 173 if (mget.mnt_special) 174 strcpy(mnt_back, mget.mnt_special); 175 } else { 176 mref.mnt_special = mref.mnt_mountp; 177 mref.mnt_mountp = NULL; 178 rewind(finp); 179 ret = getmntany(finp, &mget, &mref); 180 if (ret != -1) { 181 strcpy(mnt_front, mget.mnt_mountp); 182 if (mget.mnt_special) 183 strcpy(mnt_back, mget.mnt_special); 184 } else { 185 pr_err(gettext("warning: %s not in mnttab"), 186 mref.mnt_special); 187 } 188 } 189 fclose(finp); 190 } 191 192 /* try to get the daemon to unmount this file system for us */ 193 xx = daemon_unmount(mnt_front, flag); 194 if (xx == ENOTSUP) 195 return (1); 196 if (xx == EBUSY) { 197 pr_err(gettext("%s %s"), mnt_front, strerror(xx)); 198 return (1); 199 } 200 if (xx == EIO) { 201 /* try to unmount the file system directly */ 202 if (umount2(mnt_front, flag) == -1) { 203 pr_err(gettext("%s %s"), mnt_front, strerror(errno)); 204 return (1); 205 } 206 } 207 208 /* if we do not know the name of the back file system mount point */ 209 if (mnt_back[0] == '\0') { 210 /* all done */ 211 return (0); 212 } 213 214 /* 215 * If the back file system was mounted on a directory with a 216 * parent name of BACKMNT_NAME then we assume that we 217 * mounted it and that it is okay to try to umount it. 218 */ 219 if (strstr(mnt_back, BACKMNT_NAME) == NULL) 220 return (0); 221 222 /* if no back file system mounted */ 223 if (strcmp(mnt_back, "nobackfs") == 0) 224 return (0); 225 226 /* fork */ 227 if ((pid = fork()) == -1) { 228 pr_err(gettext("could not fork %s"), strerror(errno)); 229 return (1); 230 } 231 232 /* if the child */ 233 if (pid == 0) { 234 /* invoke the umount command on the back file system */ 235 xx = execl("/sbin/umount", "/sbin/umount", mnt_back, NULL); 236 pr_err(gettext("could not exec /sbin/umount" 237 " on back file system %s"), strerror(errno)); 238 } 239 240 /* else if the parent */ 241 else { 242 /* wait for the child to exit */ 243 if (wait(&stat_loc) == -1) { 244 pr_err(gettext("wait failed %s"), strerror(errno)); 245 return (1); 246 } 247 248 if (!WIFEXITED(stat_loc)) { 249 pr_err(gettext("back umount did not exit")); 250 return (1); 251 } 252 253 xx = WEXITSTATUS(stat_loc); 254 if (xx) { 255 pr_err(gettext("back umount failed")); 256 return (xx); 257 } 258 } 259 260 /* delete the back file system mount point since we created it */ 261 rmdir(mnt_back); 262 263 return (xx); 264 } 265 266 /* 267 * 268 * pr_err 269 * 270 * Description: 271 * Prints an error message to stderr. 272 * Arguments: 273 * fmt printf style format 274 * ... arguments for fmt 275 * Returns: 276 * Preconditions: 277 * precond(fmt) 278 */ 279 280 void 281 pr_err(char *fmt, ...) 282 { 283 va_list ap; 284 285 va_start(ap, fmt); 286 (void) fprintf(stderr, gettext("umount -F cachefs: ")); 287 (void) vfprintf(stderr, fmt, ap); 288 (void) fprintf(stderr, "\n"); 289 va_end(ap); 290 } 291 292 /* 293 * 294 * usage 295 * 296 * Description: 297 * Prints a usage message. 298 * Arguments: 299 * An optional additional message to be displayed. 300 * Returns: 301 * Preconditions: 302 */ 303 304 void 305 usage(char *msgp) 306 { 307 if (msgp) 308 pr_err(gettext("%s"), msgp); 309 fprintf(stderr, gettext("Usage: umount -F cachefs dir")); 310 } 311 312 /* 313 * 314 * daemon_unmount 315 * 316 * Description: 317 * Notifies the cachefsd of an unmount request. 318 * Arguments: 319 * Mount point to unmount. 320 * Returns: 321 * Returns 0 if the cachefsd unmounted the file system 322 * EIO if should try unmount directly 323 * EBUSY if did not unmount because busy 324 * EAGAIN if umounted but should not unmount nfs mount 325 * 326 * Preconditions: 327 * precond(mntptp) 328 */ 329 330 int 331 daemon_unmount(char *mntptp, int flag) 332 { 333 CLIENT *clnt; 334 enum clnt_stat retval; 335 int xx; 336 int result; 337 char *hostp; 338 struct utsname info; 339 static struct timeval TIMEOUT = { 60*60, 0 }; 340 static struct timeval create_timeout = { 5, 0}; 341 struct cachefsd_fs_unmounted uargs; 342 343 /* get the host name */ 344 xx = uname(&info); 345 if (xx == -1) { 346 pr_err(gettext("cannot get host name, errno %d"), errno); 347 return (EIO); 348 } 349 hostp = info.nodename; 350 uargs.mntpt = mntptp; 351 uargs.flag = flag; 352 353 /* creat the connection to the daemon */ 354 clnt = clnt_create_timed(hostp, CACHEFSDPROG, CACHEFSDVERS, "local", 355 &create_timeout); 356 if (clnt == NULL) { 357 pr_err(gettext("cachefsd is not running")); 358 return (EIO); 359 } 360 clnt_control(clnt, CLSET_TIMEOUT, (char *)&TIMEOUT); 361 362 retval = cachefsd_fs_unmounted_1(&uargs, &result, clnt); 363 if (retval != RPC_SUCCESS) { 364 clnt_perror(clnt, gettext("cachefsd is not responding")); 365 clnt_destroy(clnt); 366 return (EIO); 367 } 368 369 clnt_destroy(clnt); 370 371 return (result); 372 } 373