#include "webdavd.h"
#include "LogMessage.h"
#include <sys/types.h>
#include <sys/errno.h>
#include <sys/wait.h>
#include <sys/syslog.h>
#include <sys/un.h>
#include <sys/resource.h>
#include <sys/param.h>
#include <sys/mount.h>
#include <sys/syslimits.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <err.h>
#include <fcntl.h>
#include <paths.h>
#include <signal.h>
#include <time.h>
#include <notify.h>
#include <CoreServices/CoreServices.h>
#include <SystemConfiguration/SystemConfiguration.h>
#include <mntopts.h>
#include "webdav_authcache.h"
#include "webdav_network.h"
#include "webdav_requestqueue.h"
#include "webdav_cache.h"
unsigned int gtimeout_val;
char *gtimeout_string;
int gWebdavfsDebug = FALSE;
uid_t gProcessUID = -1;
int gSuppressAllUI = FALSE;
int gSecureServerAuth = FALSE;
char gWebdavCachePath[MAXPATHLEN + 1] = "";
int gSecureConnection = FALSE;
CFURLRef gBaseURL = NULL;
static int wakeupFDs[2] = { -1, -1 };
static char mountPoint[MAXPATHLEN];
static char mntfromname[MNAMELEN];
int getmnt_silent = 1;
#define CFENVFORMATSTRING "__CF_USER_TEXT_ENCODING=0x%X:0:0"
void webdav_debug_assert(const char *componentNameString, const char *assertionString,
const char *exceptionLabelString, const char *errorString,
const char *fileName, long lineNumber, int errorCode)
{
#pragma unused(componentNameString)
if ( (assertionString != NULL) && (*assertionString != '\0') )
{
if ( errorCode != 0 )
{
syslog(WEBDAV_LOG_LEVEL, "(%s) failed with %d%s%s%s%s; file: %s; line: %ld",
assertionString,
errorCode,
(errorString != NULL) ? "; " : "",
(errorString != NULL) ? errorString : "",
(exceptionLabelString != NULL) ? "; going to " : "",
(exceptionLabelString != NULL) ? exceptionLabelString : "",
fileName,
lineNumber);
}
else
{
syslog(WEBDAV_LOG_LEVEL, "(%s) failed%s%s%s%s; file: %s; line: %ld",
assertionString,
(errorString != NULL) ? "; " : "",
(errorString != NULL) ? errorString : "",
(exceptionLabelString != NULL) ? "; going to " : "",
(exceptionLabelString != NULL) ? exceptionLabelString : "",
fileName,
lineNumber);
}
}
else
{
syslog(WEBDAV_LOG_LEVEL, "%s; file: %s; line: %ld",
errorString,
fileName,
lineNumber);
}
}
static void usage(void)
{
(void)fprintf(stderr,
"usage: mount_webdav [-s] [-S] [-a<fd>] [-o options] [-v <volume name>]\n");
(void)fprintf(stderr,
"\t<WebDAV_URL> node\n");
}
static
char *GetMountURI(char *arguri, int *isHTTPS)
{
int hasScheme;
int hasTrailingSlash;
size_t argURILength;
char *uri;
argURILength = strlen(arguri);
*isHTTPS = (strncasecmp(arguri, "https://", strlen("https://")) == 0);
hasScheme = ((strncasecmp(arguri, "http://", strlen("http://")) == 0) || *isHTTPS);
hasTrailingSlash = arguri[argURILength - 1] == '/';
uri = malloc(argURILength +
(hasScheme ? 0 : strlen("http://")) +
(hasTrailingSlash ? 0 : 1) +
1);
require(uri != NULL, malloc_uri);
if ( !hasScheme )
{
strcpy(uri, "http://");
}
else
{
*uri = '\0';
}
strcat(uri, arguri);
if ( !hasTrailingSlash )
{
strcat(uri, "/");
}
malloc_uri:
return ( uri );
}
void webdav_kill(int message)
{
if (wakeupFDs[0] != -1)
{
if (wakeupFDs[1] != -1)
{
verify(write(wakeupFDs[1], &message, sizeof(int)) == sizeof(int));
}
}
else
{
exit(EXIT_FAILURE);
}
}
static void webdav_force_unmount(char *mntpt)
{
int pid, terminated_pid;
int result = -1;
union wait status;
pid = fork();
if (pid == 0)
{
char CFUserTextEncodingEnvSetting[sizeof(CFENVFORMATSTRING) + 20];
char *env[] = {CFUserTextEncodingEnvSetting, "", (char *) 0 };
snprintf(CFUserTextEncodingEnvSetting, sizeof(CFUserTextEncodingEnvSetting), CFENVFORMATSTRING, getuid());
result = execle(PRIVATE_UNMOUNT_COMMAND, PRIVATE_UNMOUNT_COMMAND,
PRIVATE_UNMOUNT_FLAGS, mntpt, (char *) 0, env);
goto Return;
}
require(pid != -1, Return);
while ( (terminated_pid = wait4(pid, (int *)&status, 0, NULL)) < 0 )
{
if ( errno != EINTR )
{
break;
}
}
Return:
check_noerr_string(errno, strerror(errno));
_exit(EXIT_FAILURE);
}
static int attempt_webdav_load(void)
{
int pid, terminated_pid;
int result = -1;
union wait status;
pid = fork();
if (pid == 0)
{
char CFUserTextEncodingEnvSetting[sizeof(CFENVFORMATSTRING) + 20];
char *env[] = {CFUserTextEncodingEnvSetting, "", (char *) 0 };
snprintf(CFUserTextEncodingEnvSetting, sizeof(CFUserTextEncodingEnvSetting), CFENVFORMATSTRING, getuid());
result = execle(PRIVATE_LOAD_COMMAND, PRIVATE_LOAD_COMMAND, (char *) 0, env);
goto Return;
}
require_action(pid != -1, Return, result = errno);
while ( (terminated_pid = wait4(pid, (int *)&status, 0, NULL)) < 0 )
{
if ( errno != EINTR )
{
break;
}
}
if ( (terminated_pid == pid) && (WIFEXITED(status)) )
{
result = WEXITSTATUS(status);
}
else
{
result = -1;
}
Return:
check_noerr_string(result, strerror(errno));
return result;
}
static void create_unmount_thread(void)
{
int error;
pthread_t unmount_thread;
pthread_attr_t unmount_thread_attr;
error = pthread_attr_init(&unmount_thread_attr);
require_noerr(error, pthread_attr_init);
error = pthread_attr_setdetachstate(&unmount_thread_attr, PTHREAD_CREATE_DETACHED);
require_noerr(error, pthread_attr_setdetachstate);
error = pthread_create(&unmount_thread, &unmount_thread_attr,
(void *)webdav_force_unmount, (void *)mntfromname);
require_noerr(error, pthread_create);
return;
pthread_create:
pthread_attr_setdetachstate:
pthread_attr_init:
exit(error);
}
static void create_change_thread(void)
{
int error;
pthread_t change_thread;
pthread_attr_t change_thread_attr;
error = pthread_attr_init(&change_thread_attr);
require_noerr(error, pthread_attr_init);
error = pthread_attr_setdetachstate(&change_thread_attr, PTHREAD_CREATE_DETACHED);
require_noerr(error, pthread_attr_setdetachstate);
error = pthread_create(&change_thread, &change_thread_attr,
(void *)network_update_proxy, (void *)NULL);
require_noerr(error, pthread_create);
return;
pthread_create:
pthread_attr_setdetachstate:
pthread_attr_init:
exit(error);
}
#define TMP_WEBDAV_UDS _PATH_TMP ".webdavUDS.XXXXXX"
#define WEBDAV_MAX_USERNAME_LEN 256
#define WEBDAV_MAX_PASSWORD_LEN 256
#define WEBDAV_MAX_DOMAIN_LEN 256
int main(int argc, char *argv[])
{
struct webdav_args args;
struct sockaddr_un un;
struct statfs *buffer;
int mntflags;
int servermntflags;
struct vfsconf vfc;
mode_t mode_mask;
int return_code;
int listen_socket;
int store_notify_fd;
int lowdisk_notify_fd;
int out_token;
int error;
int ch;
int i;
int count;
unsigned int mntfromnameLength;
struct rlimit rlp;
struct node_entry *root_node;
char volumeName[NAME_MAX + 1] = "";
char *uri;
int mirrored_mount;
int isMounted = FALSE;
mntoptparse_t mp;
char user[WEBDAV_MAX_USERNAME_LEN];
char pass[WEBDAV_MAX_PASSWORD_LEN];
char domain[WEBDAV_MAX_DOMAIN_LEN];
struct webdav_request_statfs request_statfs;
struct webdav_reply_statfs reply_statfs;
int tempError;
struct statfs buf;
error = 0;
gProcessUID = getuid();
user[0] = '\0';
pass[0] = '\0';
domain[0] = '\0';
mntflags = 0;
while ((ch = getopt(argc, argv, "sSa:o:v:")) != -1)
{
switch (ch)
{
case 'a':
{
int fd = atoi(optarg),
zero = 0;
uint32_t be_len1, be_len2, be_len3;
size_t len1, len2, len3;
if (fd >= 0 && lseek(fd, 0LL, SEEK_SET) != -1)
{
if (read(fd, &be_len1, sizeof be_len1) == sizeof be_len1 &&
(len1 = ntohl(be_len1)) > 0 &&
len1 < WEBDAV_MAX_USERNAME_LEN)
{
if (read(fd, user, len1) > 0)
{
user[len1] = '\0';
if (read(fd, &be_len2, sizeof be_len2) == sizeof be_len2 &&
(len2 = ntohl(be_len2)) > 0 &&
len2 < WEBDAV_MAX_PASSWORD_LEN)
{
if (read(fd, pass, len2) > 0)
{
pass[len2] = '\0';
if (read(fd, &be_len3, sizeof be_len3) == sizeof be_len3 &&
(len3 = ntohl(be_len3)) > 0 &&
len3 < WEBDAV_MAX_DOMAIN_LEN)
{
if (read(fd, domain, len3) > 0)
{
domain[len3] = '\0';
}
}
}
}
}
}
if ( (fd != STDIN_FILENO) &&
(fd != STDOUT_FILENO) &&
(fd != STDERR_FILENO) )
{
struct stat statb;
off_t bytes_to_overwrite;
size_t bytes_to_write;
if (fstat(fd, &statb) != -1) {
bytes_to_overwrite = statb.st_size;
(void)lseek(fd, 0LL, SEEK_SET);
while (bytes_to_overwrite != 0) {
bytes_to_write = bytes_to_overwrite;
if (bytes_to_write > sizeof zero)
bytes_to_write = sizeof zero;
if (write(fd, (char *) & zero, bytes_to_write) < 0)
{
break;
}
bytes_to_overwrite -= bytes_to_write;
}
(void)fsync(fd);
}
(void)close(fd);
}
}
break;
}
case 'S':
gSuppressAllUI = 1;
break;
case 's':
gSecureServerAuth = 1;
break;
case 'o':
{
const struct mntopt mopts[] = {
MOPT_USERQUOTA,
MOPT_GROUPQUOTA,
MOPT_FSTAB_COMPAT,
MOPT_NODEV,
MOPT_NOEXEC,
MOPT_NOSUID,
MOPT_RDONLY,
MOPT_UNION,
MOPT_BROWSE,
MOPT_AUTOMOUNTED,
MOPT_QUARANTINE,
{ NULL, 0, 0, 0 }
};
mp = getmntopts(optarg, mopts, &mntflags, 0);
if (mp == NULL)
error = 1;
else
freemntopts(mp);
}
break;
case 'v':
if ( strlen(optarg) <= NAME_MAX )
{
strcpy(volumeName, optarg);
}
else
{
error = 1;
}
break;
default:
error = 1;
break;
}
}
if (!error)
{
if (optind != (argc - 2) || strlen(argv[optind]) > MAXPATHLEN)
{
error = 1;
}
}
require_noerr_action_quiet(error, error_exit, error = EINVAL; usage());
mirrored_mount = gSuppressAllUI && (mntflags & MNT_DONTBROWSE);
require_action(realpath(argv[optind + 1], mountPoint) != NULL, error_exit, error = ENOENT);
if ( *volumeName == '\0' )
{
strcpy(volumeName, strrchr(mountPoint, '/') + 1);
}
uri = GetMountURI(argv[optind], &gSecureConnection);
require_action_quiet(uri != NULL, error_exit, error = EINVAL);
strncpy(mntfromname, uri , MNAMELEN);
mntfromname[MNAMELEN] = '\0';
count = getmntinfo(&buffer, MNT_NOWAIT);
mntfromnameLength = strlen(mntfromname);
for (i = 0; i < count; i++)
{
if ( (buffer[i].f_owner == gProcessUID) &&
(strcmp("webdav", buffer[i].f_fstypename) == 0) &&
(strlen(buffer[i].f_mntfromname) == mntfromnameLength) &&
(strncasecmp(buffer[i].f_mntfromname, mntfromname, mntfromnameLength) == 0) )
{
if(((buffer[i].f_flags & MNT_DONTBROWSE) && (mntflags & MNT_DONTBROWSE)) ||
(!(buffer[i].f_flags & MNT_DONTBROWSE) && !(mntflags & MNT_DONTBROWSE)))
{
LogMessage(kSysLog | kError, "%s is already mounted: %s\n", mntfromname, strerror(EBUSY));
error = EBUSY;
goto error_exit;
}
}
}
gWebdavfsDebug = (getenv("WEBDAVFS_DEBUG") != NULL);
if ( setsid() < 0 )
{
debug_string("setsid() failed");
}
(void)chdir("/");
if ( !gWebdavfsDebug )
{
int fd;
fd = open(_PATH_DEVNULL, O_RDWR, 0);
if (fd != -1)
{
(void)dup2(fd, STDIN_FILENO);
(void)dup2(fd, STDOUT_FILENO);
(void)dup2(fd, STDERR_FILENO);
if (fd > 2)
{
(void)close(fd);
}
}
}
CFRunLoopGetCurrent();
gtimeout_string = WEBDAV_PULSE_TIMEOUT;
gtimeout_val = atoi(gtimeout_string);
error = getrlimit(RLIMIT_NOFILE, &rlp);
require_noerr_action(error, error_exit, error = EINVAL);
closelog();
for (i = 0; i < (int)rlp.rlim_cur; ++i)
{
switch (i)
{
case STDIN_FILENO:
case STDOUT_FILENO:
case STDERR_FILENO:
break;
default:
(void)close(i);
}
}
openlog("webdavfs_agent", LOG_CONS | LOG_PID, LOG_DAEMON);
if ( rlp.rlim_cur < WEBDAV_RLIMIT_NOFILE )
{
rlp.rlim_cur = WEBDAV_RLIMIT_NOFILE;
error = setrlimit(RLIMIT_NOFILE, &rlp);
require_noerr_action(error, error_exit, error = EINVAL);
}
signal(SIGPIPE, SIG_IGN);
vfc.vfc_typenum = -1;
error = getvfsbyname("webdav", &vfc);
if (error)
{
error = attempt_webdav_load();
require_noerr_action_quiet(error, error_exit, error = EINVAL);
error = getvfsbyname("webdav", &vfc);
}
require_noerr_action(error, error_exit, error = EINVAL);
error = nodecache_init(strlen(uri), uri, &root_node);
require_noerr_action_quiet(error, error_exit, error = EINVAL);
error = authcache_init(user, pass, domain);
require_noerr_action_quiet(error, error_exit, error = EINVAL);
bzero(user, sizeof(user));
bzero(pass, sizeof(pass));
bzero(domain, sizeof(domain));
error = network_init((const UInt8 *)uri, strlen(uri), &store_notify_fd, mirrored_mount);
free(uri);
uri = NULL;
require_noerr_action_quiet(error, error_exit, error = EINVAL);
error = filesystem_init(vfc.vfc_typenum);
require_noerr_action_quiet(error, error_exit, error = EINVAL);
error = requestqueue_init();
require_noerr_action_quiet(error, error_exit, error = EINVAL);
servermntflags = 0;
error = filesystem_mount(&servermntflags);
require_noerr_quiet(error, error_exit);
mntflags |= servermntflags;
listen_socket = socket(PF_LOCAL, SOCK_STREAM, 0);
require_action(listen_socket >= 0, error_exit, error = EINVAL);
bzero(&un, sizeof(un));
un.sun_len = sizeof(un);
un.sun_family = AF_LOCAL;
require_action(sizeof(TMP_WEBDAV_UDS) < sizeof(un.sun_path), error_exit, error = EINVAL);
strcpy(un.sun_path, TMP_WEBDAV_UDS);
require_action(mktemp(un.sun_path) != NULL, error_exit, error = EINVAL);
mode_mask = umask(0555);
require_action(bind(listen_socket, (struct sockaddr *)&un, sizeof(un)) >= 0, error_exit, error = EINVAL);
(void)umask(mode_mask);
require_action(chflags(un.sun_path, UF_IMMUTABLE) == 0, error_exit, error = EINVAL);
require_noerr_action(listen(listen_socket, WEBDAV_MAX_KEXT_CONNECTIONS), error_exit, error = EINVAL);
require_action(pipe(wakeupFDs) == 0, error_exit, wakeupFDs[0] = wakeupFDs[1] = -1; error = EINVAL);
signal(SIGHUP, webdav_kill);
signal(SIGINT, webdav_kill);
signal(SIGQUIT, webdav_kill);
signal(SIGILL, webdav_kill);
signal(SIGTRAP, webdav_kill);
signal(SIGABRT, webdav_kill);
signal(SIGEMT, webdav_kill);
signal(SIGFPE, webdav_kill);
signal(SIGBUS, webdav_kill);
signal(SIGSEGV, webdav_kill);
signal(SIGSYS, webdav_kill);
signal(SIGALRM, webdav_kill);
signal(SIGTSTP, webdav_kill);
signal(SIGTTIN, webdav_kill);
signal(SIGTTOU, webdav_kill);
signal(SIGXCPU, webdav_kill);
signal(SIGXFSZ, webdav_kill);
signal(SIGVTALRM, webdav_kill);
signal(SIGPROF, webdav_kill);
signal(SIGUSR1, webdav_kill);
signal(SIGUSR2, webdav_kill);
args.pa_mntfromname = mntfromname;
args.pa_version = kCurrentWebdavArgsVersion;
args.pa_socket_namelen = sizeof(un);
args.pa_socket_name = (struct sockaddr *)&un;
args.pa_vol_name = volumeName;
args.pa_flags = 0;
if ( gSuppressAllUI )
{
args.pa_flags |= WEBDAV_SUPPRESSALLUI;
}
if ( gSecureConnection )
{
args.pa_flags |= WEBDAV_SECURECONNECTION;
}
args.pa_root_id = root_node->nodeid;
args.pa_root_fileid = WEBDAV_ROOTFILEID;
args.pa_dir_size = WEBDAV_DIR_SIZE;
args.pa_link_max = 1;
args.pa_name_max = NAME_MAX;
args.pa_path_max = PATH_MAX;
args.pa_pipe_buf = -1;
args.pa_chown_restricted = _POSIX_CHOWN_RESTRICTED;
args.pa_no_trunc = _POSIX_NO_TRUNC;
bzero(&reply_statfs, sizeof(struct webdav_reply_statfs));
request_statfs.pcr.pcr_uid = getuid();
request_statfs.pcr.pcr_ngroups = getgroups(NGROUPS_MAX, request_statfs.pcr.pcr_groups);
request_statfs.root_obj_id = root_node->nodeid;
tempError = filesystem_statfs(&request_statfs, &reply_statfs);
memset (&args.pa_vfsstatfs, 0, sizeof (args.pa_vfsstatfs));
if (tempError == 0) {
args.pa_vfsstatfs.f_bsize = reply_statfs.fs_attr.f_bsize;
args.pa_vfsstatfs.f_iosize = reply_statfs.fs_attr.f_iosize;
args.pa_vfsstatfs.f_blocks = reply_statfs.fs_attr.f_blocks;
args.pa_vfsstatfs.f_bfree = reply_statfs.fs_attr.f_bfree;
args.pa_vfsstatfs.f_bavail = reply_statfs.fs_attr.f_bavail;
args.pa_vfsstatfs.f_files = reply_statfs.fs_attr.f_files;
args.pa_vfsstatfs.f_ffree = reply_statfs.fs_attr.f_ffree;
}
tempError = statfs(_PATH_TMP, &buf);
if ( tempError == 0 )
args.pa_vfsstatfs.f_iosize = buf.f_iosize;
return_code = mount(vfc.vfc_name, mountPoint, mntflags, &args);
require_noerr_action(return_code, error_exit, error = errno);
kill(getppid(), SIGTERM);
isMounted = TRUE;
signal(SIGTERM, webdav_kill);
syslog(LOG_INFO, "%s mounted", mountPoint);
{
struct sockaddr_in socket_address;
SCNetworkReachabilityRef reachabilityRef;
bzero(&socket_address, sizeof(socket_address));
socket_address.sin_len = sizeof(socket_address);
socket_address.sin_family = AF_INET;
socket_address.sin_port = 0;
socket_address.sin_addr.s_addr = INADDR_LOOPBACK;
reachabilityRef = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (struct sockaddr*)&socket_address);
if (reachabilityRef != NULL)
{
SCNetworkReachabilityScheduleWithRunLoop(reachabilityRef, CFRunLoopGetCurrent(), CFSTR("_nonexistent_"));
}
}
tempError = notify_register_file_descriptor("com.apple.system.lowdiskspace.system", &lowdisk_notify_fd, 0, &out_token);
if (tempError != NOTIFY_STATUS_OK)
{
syslog(LOG_ERR, "failed to register for low disk space notifications: err = %u\n", tempError);
lowdisk_notify_fd = -1;
}
while ( TRUE )
{
int accept_socket;
fd_set readfds;
int max_select_fd;
FD_ZERO(&readfds);
FD_SET(listen_socket, &readfds);
FD_SET(store_notify_fd, &readfds);
max_select_fd = MAX(listen_socket, store_notify_fd);
if ( lowdisk_notify_fd != -1 )
{
FD_SET(lowdisk_notify_fd, &readfds);
max_select_fd = MAX(lowdisk_notify_fd, max_select_fd);
}
if (wakeupFDs[0] != -1)
{
FD_SET(wakeupFDs[0], &readfds);
max_select_fd = MAX(wakeupFDs[0], max_select_fd);
}
++max_select_fd;
return_code = select(max_select_fd, &readfds, (fd_set *)0, (fd_set *)0, (struct timeval *)0);
if (return_code <= 0)
{
error = errno;
if (error != EINTR)
LogMessage(kSysLog | kError, "webdavfs_agent exiting on select errno %d for %s\n", error, mountPoint);
require(error == EINTR, error_exit);
continue;
}
if ( (wakeupFDs[0] != -1) && FD_ISSET(wakeupFDs[0], &readfds) )
{
int message;
message = -1;
(void)read(wakeupFDs[0], &message, sizeof(int));
if (message == SIGHUP)
{
}
else
{
wakeupFDs[0] = -1;
if ( message >= 0 )
{
LogMessage(kSysLog | kError, "webdavfs_agent received signal: %d. Unmounting %s\n", message, mountPoint);
}
else
{
if ( message == -2 )
{
LogMessage(kSysLog | kError, "webdavfs_agent received unmount message for %s\n", mountPoint);
break;
}
else
{
LogMessage(kSysLog | kError, "webdavfs_agent received message: %d. Force unmounting %s\n", message, mountPoint);
}
}
create_unmount_thread();
}
}
if ( FD_ISSET(listen_socket, &readfds) )
{
struct sockaddr_un addr;
socklen_t addrlen;
addrlen = sizeof(addr);
accept_socket = accept(listen_socket, (struct sockaddr *)&addr, &addrlen);
if (accept_socket < 0)
{
error = errno;
require(error == EINTR, error_exit);
continue;
}
error = requestqueue_enqueue_request(accept_socket);
require_noerr_quiet(error, error_exit);
}
if ( FD_ISSET(store_notify_fd, &readfds) )
{
ssize_t bytes_read;
int32_t buf;
bytes_read = read(store_notify_fd, &buf, sizeof(buf));
create_change_thread();
}
if ( lowdisk_notify_fd != -1 )
{
if ( FD_ISSET(lowdisk_notify_fd, &readfds) )
{
ssize_t bytes_read;
bytes_read = read(lowdisk_notify_fd, &out_token, sizeof(out_token));
(void) requestqueue_purge_cache_files();
}
}
}
LogMessage(kTrace, "%s unmounted\n", mountPoint);
if (*gWebdavCachePath != '\0')
{
(void) rmdir(gWebdavCachePath);
}
(void) chflags(un.sun_path, 0);
(void) unlink(un.sun_path);
exit(EXIT_SUCCESS);
error_exit:
if ( !isMounted )
{
switch (error)
{
case ENOENT:
break;
case ECANCELED:
break;
case EBUSY:
break;
case ENODEV:
break;
default:
error = EINVAL;
break;
}
}
exit(error);
}