298 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			298 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* vi: set sw=4 ts=4: */
 | |
| /*
 | |
|  * Mini umount implementation for busybox
 | |
|  *
 | |
|  * Copyright (C) 1999,2000 by Lineo, inc. and Erik Andersen
 | |
|  * Copyright (C) 1999,2000,2001 by Erik Andersen <andersee@debian.org>
 | |
|  *
 | |
|  * This program is free software; you can redistribute it and/or modify
 | |
|  * it under the terms of the GNU General Public License as published by
 | |
|  * the Free Software Foundation; either version 2 of the License, or
 | |
|  * (at your option) any later version.
 | |
|  *
 | |
|  * This program is distributed in the hope that it will be useful,
 | |
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 | |
|  * General Public License for more details.
 | |
|  *
 | |
|  * You should have received a copy of the GNU General Public License
 | |
|  * along with this program; if not, write to the Free Software
 | |
|  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 | |
|  *
 | |
|  */
 | |
| 
 | |
| #include <limits.h>
 | |
| #include <stdio.h>
 | |
| #include <mntent.h>
 | |
| #include <errno.h>
 | |
| #include <string.h>
 | |
| #include <stdlib.h>
 | |
| #include "busybox.h"
 | |
| 
 | |
| /* Teach libc5 about realpath -- it includes it but the 
 | |
|  * prototype is missing... */
 | |
| #if (__GLIBC__ <= 2) && (__GLIBC_MINOR__ < 1)
 | |
| extern char *realpath(const char *path, char *resolved_path);
 | |
| #endif
 | |
| 
 | |
| static const int MNT_FORCE = 1;
 | |
| static const int MS_MGC_VAL = 0xc0ed0000; /* Magic number indicatng "new" flags */
 | |
| static const int MS_REMOUNT = 32;	/* Alter flags of a mounted FS.  */
 | |
| static const int MS_RDONLY = 1;	/* Mount read-only.  */
 | |
| 
 | |
| extern int mount (__const char *__special_file, __const char *__dir,
 | |
| 			__const char *__fstype, unsigned long int __rwflag,
 | |
| 			__const void *__data);
 | |
| extern int umount (__const char *__special_file);
 | |
| extern int umount2 (__const char *__special_file, int __flags);
 | |
| 
 | |
| struct _mtab_entry_t {
 | |
| 	char *device;
 | |
| 	char *mountpt;
 | |
| 	struct _mtab_entry_t *next;
 | |
| };
 | |
| 
 | |
| static struct _mtab_entry_t *mtab_cache = NULL;
 | |
| 
 | |
| 
 | |
| 
 | |
| #if defined CONFIG_FEATURE_MOUNT_FORCE
 | |
| static int doForce = FALSE;
 | |
| #endif
 | |
| #if defined CONFIG_FEATURE_MOUNT_LOOP
 | |
| static int freeLoop = TRUE;
 | |
| #endif
 | |
| #if defined CONFIG_FEATURE_MTAB_SUPPORT
 | |
| static int useMtab = TRUE;
 | |
| #endif
 | |
| static int umountAll = FALSE;
 | |
| static int doRemount = FALSE;
 | |
| extern const char mtab_file[];	/* Defined in utility.c */
 | |
| 
 | |
| 
 | |
| 
 | |
| /* These functions are here because the getmntent functions do not appear
 | |
|  * to be re-entrant, which leads to all sorts of problems when we try to
 | |
|  * use them recursively - randolph
 | |
|  *
 | |
|  * TODO: Perhaps switch to using Glibc's getmntent_r
 | |
|  *        -Erik
 | |
|  */
 | |
| static void mtab_read(void)
 | |
| {
 | |
| 	struct _mtab_entry_t *entry = NULL;
 | |
| 	struct mntent *e;
 | |
| 	FILE *fp;
 | |
| 
 | |
| 	if (mtab_cache != NULL)
 | |
| 		return;
 | |
| 
 | |
| 	if ((fp = setmntent(mtab_file, "r")) == NULL) {
 | |
| 		error_msg("Cannot open %s", mtab_file);
 | |
| 		return;
 | |
| 	}
 | |
| 	while ((e = getmntent(fp))) {
 | |
| 		entry = xmalloc(sizeof(struct _mtab_entry_t));
 | |
| 		entry->device = strdup(e->mnt_fsname);
 | |
| 		entry->mountpt = strdup(e->mnt_dir);
 | |
| 		entry->next = mtab_cache;
 | |
| 		mtab_cache = entry;
 | |
| 	}
 | |
| 	endmntent(fp);
 | |
| }
 | |
| 
 | |
| static char *mtab_getinfo(const char *match, const char which)
 | |
| {
 | |
| 	struct _mtab_entry_t *cur = mtab_cache;
 | |
| 
 | |
| 	while (cur) {
 | |
| 		if (strcmp(cur->mountpt, match) == 0 ||
 | |
| 			strcmp(cur->device, match) == 0) {
 | |
| 			if (which == MTAB_GETMOUNTPT) {
 | |
| 				return cur->mountpt;
 | |
| 			} else {
 | |
| #if !defined CONFIG_FEATURE_MTAB_SUPPORT
 | |
| 				if (strcmp(cur->device, "/dev/root") == 0) {
 | |
| 					/* Adjusts device to be the real root device,
 | |
| 					 * or leaves device alone if it can't find it */
 | |
| 					cur->device = find_real_root_device_name(cur->device);
 | |
| 				}
 | |
| #endif
 | |
| 				return cur->device;
 | |
| 			}
 | |
| 		}
 | |
| 		cur = cur->next;
 | |
| 	}
 | |
| 	return NULL;
 | |
| }
 | |
| 
 | |
| static char *mtab_next(void **iter)
 | |
| {
 | |
| 	char *mp;
 | |
| 
 | |
| 	if (iter == NULL || *iter == NULL)
 | |
| 		return NULL;
 | |
| 	mp = ((struct _mtab_entry_t *) (*iter))->mountpt;
 | |
| 	*iter = (void *) ((struct _mtab_entry_t *) (*iter))->next;
 | |
| 	return mp;
 | |
| }
 | |
| 
 | |
| static char *mtab_first(void **iter)
 | |
| {
 | |
| 	struct _mtab_entry_t *mtab_iter;
 | |
| 
 | |
| 	if (!iter)
 | |
| 		return NULL;
 | |
| 	mtab_iter = mtab_cache;
 | |
| 	*iter = (void *) mtab_iter;
 | |
| 	return mtab_next(iter);
 | |
| }
 | |
| 
 | |
| /* Don't bother to clean up, since exit() does that 
 | |
|  * automagically, so we can save a few bytes */
 | |
| #ifdef CONFIG_FEATURE_CLEAN_UP
 | |
| static void mtab_free(void)
 | |
| {
 | |
| 	struct _mtab_entry_t *this, *next;
 | |
| 
 | |
| 	this = mtab_cache;
 | |
| 	while (this) {
 | |
| 		next = this->next;
 | |
| 		if (this->device)
 | |
| 			free(this->device);
 | |
| 		if (this->mountpt)
 | |
| 			free(this->mountpt);
 | |
| 		free(this);
 | |
| 		this = next;
 | |
| 	}
 | |
| }
 | |
| #endif
 | |
| 
 | |
| static int do_umount(const char *name)
 | |
| {
 | |
| 	int status;
 | |
| 	char *blockDevice = mtab_getinfo(name, MTAB_GETDEVICE);
 | |
| 
 | |
| 	if (blockDevice && strcmp(blockDevice, name) == 0)
 | |
| 		name = mtab_getinfo(blockDevice, MTAB_GETMOUNTPT);
 | |
| 
 | |
| 	status = umount(name);
 | |
| 
 | |
| #if defined CONFIG_FEATURE_MOUNT_LOOP
 | |
| 	if (freeLoop == TRUE && blockDevice != NULL && !strncmp("/dev/loop", blockDevice, 9))
 | |
| 		/* this was a loop device, delete it */
 | |
| 		del_loop(blockDevice);
 | |
| #endif
 | |
| #if defined CONFIG_FEATURE_MOUNT_FORCE
 | |
| 	if (status != 0 && doForce == TRUE) {
 | |
| 		status = umount2(blockDevice, MNT_FORCE);
 | |
| 		if (status != 0) {
 | |
| 			error_msg_and_die("forced umount of %s failed!", blockDevice);
 | |
| 		}
 | |
| 	}
 | |
| #endif
 | |
| 	if (status != 0 && doRemount == TRUE && errno == EBUSY) {
 | |
| 		status = mount(blockDevice, name, NULL,
 | |
| 					   MS_MGC_VAL | MS_REMOUNT | MS_RDONLY, NULL);
 | |
| 		if (status == 0) {
 | |
| 			error_msg("%s busy - remounted read-only", blockDevice);
 | |
| 		} else {
 | |
| 			error_msg("Cannot remount %s read-only", blockDevice);
 | |
| 		}
 | |
| 	}
 | |
| 	if (status == 0) {
 | |
| #if defined CONFIG_FEATURE_MTAB_SUPPORT
 | |
| 		if (useMtab == TRUE)
 | |
| 			erase_mtab(name);
 | |
| #endif
 | |
| 		return (TRUE);
 | |
| 	}
 | |
| 	return (FALSE);
 | |
| }
 | |
| 
 | |
| static int umount_all(void)
 | |
| {
 | |
| 	int status = TRUE;
 | |
| 	char *mountpt;
 | |
| 	void *iter;
 | |
| 
 | |
| 	for (mountpt = mtab_first(&iter); mountpt; mountpt = mtab_next(&iter)) {
 | |
| 		/* Never umount /proc on a umount -a */
 | |
| 		if (strstr(mountpt, "proc")!= NULL)
 | |
| 			continue;
 | |
| 		if (!do_umount(mountpt)) {
 | |
| 			/* Don't bother retrying the umount on busy devices */
 | |
| 			if (errno == EBUSY) {
 | |
| 				perror_msg("%s", mountpt);
 | |
| 				status = FALSE;
 | |
| 				continue;
 | |
| 			}
 | |
| 			if (!do_umount(mountpt)) {
 | |
| 				printf("Couldn't umount %s on %s: %s\n",
 | |
| 					   mountpt, mtab_getinfo(mountpt, MTAB_GETDEVICE),
 | |
| 					   strerror(errno));
 | |
| 				status = FALSE;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	return (status);
 | |
| }
 | |
| 
 | |
| extern int umount_main(int argc, char **argv)
 | |
| {
 | |
| 	char path[PATH_MAX];
 | |
| 
 | |
| 	if (argc < 2) {
 | |
| 		show_usage();
 | |
| 	}
 | |
| #ifdef CONFIG_FEATURE_CLEAN_UP
 | |
| 	atexit(mtab_free);
 | |
| #endif
 | |
| 
 | |
| 	/* Parse any options */
 | |
| 	while (--argc > 0 && **(++argv) == '-') {
 | |
| 		while (*++(*argv))
 | |
| 			switch (**argv) {
 | |
| 			case 'a':
 | |
| 				umountAll = TRUE;
 | |
| 				break;
 | |
| #if defined CONFIG_FEATURE_MOUNT_LOOP
 | |
| 			case 'l':
 | |
| 				freeLoop = FALSE;
 | |
| 				break;
 | |
| #endif
 | |
| #ifdef CONFIG_FEATURE_MTAB_SUPPORT
 | |
| 			case 'n':
 | |
| 				useMtab = FALSE;
 | |
| 				break;
 | |
| #endif
 | |
| #ifdef CONFIG_FEATURE_MOUNT_FORCE
 | |
| 			case 'f':
 | |
| 				doForce = TRUE;
 | |
| 				break;
 | |
| #endif
 | |
| 			case 'r':
 | |
| 				doRemount = TRUE;
 | |
| 				break;
 | |
| 			case 'v':
 | |
| 				break; /* ignore -v */
 | |
| 			default:
 | |
| 				show_usage();
 | |
| 			}
 | |
| 	}
 | |
| 
 | |
| 	mtab_read();
 | |
| 	if (umountAll == TRUE) {
 | |
| 		if (umount_all() == TRUE)
 | |
| 			return EXIT_SUCCESS;
 | |
| 		else
 | |
| 			return EXIT_FAILURE;
 | |
| 	}
 | |
| 	if (realpath(*argv, path) == NULL)
 | |
| 		perror_msg_and_die("%s", path);
 | |
| 	if (do_umount(path) == TRUE)
 | |
| 		return EXIT_SUCCESS;
 | |
| 	perror_msg_and_die("%s", *argv);
 | |
| }
 | |
| 
 |