#include "webdavd.h"
#include "LogMessage.h"
#include <mach/mach.h>
#include <mach/mach_error.h>
#include <servers/bootstrap.h>
#include <sys/types.h>
#include <sys/sysctl.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 <readpassphrase.h>
#include <signal.h>
#include <time.h>
#include <notify.h>
#include <sandbox.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"
#include "load_webdavfs.h"
#include "webdavfs_load_kext.h"
#include "webdav_cookie.h"
#include "webdav_utils.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;
CFStringRef gBasePath = NULL;
char gBasePathStr[MAXPATHLEN];
uint32_t gServerIdent = 0;
fsid_t g_fsid;
boolean_t gUrlIsIdisk = FALSE;
char g_mountPoint[MAXPATHLEN];
static int wakeupFDs[2] = { -1, -1 };
static char mntfromname[MNAMELEN];
char *idisk_server_names[] = {"idisk.mac.com", "idisk.me.com", NULL};
int getmnt_silent = 1;
uint64_t webdavCacheMaximumSize = WEBDAV_DEFAULT_CACHE_MAX_SIZE;
static void setCacheMaximumSize(void);
#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, uint64_t 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,
(int)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 [-i] [-s] [-S] [-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 httpStr[] = "http://";
size_t httpLength;
char *uri;
size_t URILength;
argURILength = strlen(arguri);
httpLength = strlen(httpStr);
*isHTTPS = (strncasecmp(arguri, "https://", strlen("https://")) == 0);
hasScheme = ((strncasecmp(arguri, httpStr, httpLength) == 0) || *isHTTPS);
hasTrailingSlash = arguri[argURILength - 1] == '/';
URILength = argURILength + (hasScheme ? 0 : httpLength) + (hasTrailingSlash ? 0 : 1);
uri = malloc(URILength + 1);
require(uri != NULL, malloc_uri);
if ( !hasScheme )
{
strlcpy(uri, "http://", URILength + 1);
}
else
{
*uri = '\0';
}
strlcat(uri, arguri, URILength + 1);
if ( !hasTrailingSlash )
{
strlcat(uri, "/", URILength + 1);
}
malloc_uri:
return ( uri );
}
static boolean_t urlIsIdisk(const char *url) {
boolean_t found_idisk;
size_t len, shortest_len, url_len;
char* colon;
char** idisk_server;
found_idisk = FALSE;
if (url == NULL)
return (found_idisk);
colon = strchr(url, ':');
if (colon == NULL)
return (found_idisk);
idisk_server = idisk_server_names;
shortest_len = strlen(*idisk_server);
while (*idisk_server != NULL) {
len = strlen(*idisk_server);
if (len < shortest_len)
shortest_len = len;
idisk_server++;
}
if (strlen(colon) < shortest_len)
return (found_idisk);
colon += 3;
url_len = strlen(colon);
idisk_server = idisk_server_names;
while (*idisk_server) {
len = strlen(*idisk_server);
if (url_len >= len) {
if ( strncasecmp(colon, *idisk_server, len) == 0 ) {
found_idisk = TRUE;
break;
}
}
idisk_server++;
}
return (found_idisk);
}
static OSStatus KeychainItemCopyAccountPassword(SecKeychainItemRef itemRef,
char *username,
size_t user_size,
char *password,
size_t pass_size)
{
OSStatus result;
SecKeychainAttribute attr;
SecKeychainAttributeList attrList;
UInt32 length;
void *outData;
attr.tag = kSecAccountItemAttr;
attr.length = 0;
attr.data = NULL;
attrList.count = 1;
attrList.attr = &attr;
result = SecKeychainItemCopyContent(itemRef, NULL, &attrList, &length, &outData);
if ( result == noErr )
{
if ( attr.length >= user_size || length >= pass_size ) {
syslog(LOG_ERR, "%s: keychain username or password is too long!", __FUNCTION__);
result = ENAMETOOLONG;
} else {
(void)strlcpy(username, attr.data, user_size);
(void)strlcpy(password, outData, pass_size);
}
(void) SecKeychainItemFreeContent(&attrList, outData);
}
return ( result );
}
static SecProtocolType getSecurityProtocol(CFStringRef str)
{
if ( CFStringCompare(str, CFSTR("http"), kCFCompareCaseInsensitive) == 0 )
return kSecProtocolTypeHTTP;
else if ( CFStringCompare(str, CFSTR("https"), kCFCompareCaseInsensitive) == 0 )
return kSecProtocolTypeHTTPS;
else
return kSecProtocolTypeAny;
}
static int get_keychain_credentials(char* in_url,
char* out_user,
size_t out_user_size,
char* out_pass,
size_t out_pass_size)
{
CFStringRef cfhostName = NULL;
CFStringRef cfpath = NULL;
CFStringRef cfscheme = NULL;
CFURLRef cf_url = NULL;
SecKeychainItemRef itemRef = NULL;
SecProtocolType protocol;
char* path = NULL;
char* hostName = NULL;
int result;
if ( in_url == NULL|| out_user == NULL || out_pass == NULL)
return EINVAL;
cf_url = CFURLCreateWithBytes(NULL, (const UInt8 *)in_url, strlen(in_url), kCFStringEncodingUTF8, NULL);
if ( cf_url == NULL )
return ENOMEM;
cfscheme = CFURLCopyScheme(cf_url);
if ( cfscheme == NULL ) {
result = ENOMEM;
goto cleanup_exit;
}
protocol = getSecurityProtocol(cfscheme);
cfhostName = CFURLCopyHostName(cf_url);
if ( cfhostName == NULL || (hostName = createUTF8CStringFromCFString(cfhostName)) == NULL) {
result = ENOMEM;
goto cleanup_exit;
}
cfpath = CFURLCopyPath(cf_url);
if ( cfpath == NULL || (path = createUTF8CStringFromCFString(cfpath)) == NULL) {
result = ENOMEM;
goto cleanup_exit;
}
result = SecKeychainFindInternetPassword(NULL,
(UInt32)strlen(hostName), hostName,
0, NULL,
0, NULL,
(UInt32)strlen(path), path,
0,
protocol,
kSecAuthenticationTypeAny,
0, NULL,
&itemRef);
if ( result != noErr ) {
result = SecKeychainFindInternetPassword(NULL,
(UInt32)strlen(hostName), hostName,
0, NULL,
0, NULL,
0, NULL,
0,
protocol,
kSecAuthenticationTypeAny,
0, NULL,
&itemRef);
}
if ( result == noErr ) {
result = KeychainItemCopyAccountPassword(itemRef, out_user, out_user_size, out_pass, out_pass_size);
}
cleanup_exit:
CFReleaseNull(cf_url);
CFReleaseNull(cfscheme);
CFReleaseNull(cfhostName);
CFReleaseNull(cfpath);
CFReleaseNull(itemRef);
if (path) free(path);
if (hostName) free(hostName);
return result;
}
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 error = 0;
mach_port_t mp = MACH_PORT_NULL;
error = bootstrap_look_up(bootstrap_port, (char *)WEBDAVFS_LOAD_KEXT_BOOTSTRAP_NAME, &mp);
if (error != KERN_SUCCESS) {
syslog(LOG_ERR, "%s: bootstrap_look_up: %s", __FUNCTION__, bootstrap_strerror(error));
return error;
}
error = load_kext(mp, (string_t)WEBDAVFS_VFSNAME);
if (error != KERN_SUCCESS)
syslog(LOG_ERR, "%s: load_kext: %s", __FUNCTION__, bootstrap_strerror(error));
return error;
}
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);
}
static void setCacheMaximumSize(void)
{
int mib[2];
size_t len;
int result;
uint64_t memsize;
mib[0] = CTL_HW;
mib[1] = HW_MEMSIZE;
len = sizeof(uint64_t);
result = sysctl(mib, 2, &memsize, &len, 0, 0);
if (result == 0) {
if (memsize <= WEBDAV_ONE_GIGABYTE) {
webdavCacheMaximumSize = memsize / 8;
}
else {
webdavCacheMaximumSize = memsize / 4;
}
}
else {
webdavCacheMaximumSize = WEBDAV_DEFAULT_CACHE_MAX_SIZE;
}
}
#define TMP_WEBDAV_UDS _PATH_TMP ".webdavUDS.XXXXXX"
#define WEBDAV_MAX_USERNAME_LEN 256
#define WEBDAV_MAX_PASSWORD_LEN 256
#define PASS_PROMPT "Password: "
#define USER_PROMPT "Username: "
static boolean_t readCredentialsFromFile(int fd, char *userName, char *userPassword,
char *proxyUserName, char *proxyUserPassword)
{
boolean_t success;
uint32_t be_len;
size_t len1;
ssize_t rlen;
success = TRUE;
if (fd < 0) {
syslog(LOG_ERR, "%s: invalid file descriptor arg", __FUNCTION__);
return FALSE;
}
if (lseek(fd, 0LL, SEEK_SET) == -1) {
return FALSE;
}
rlen = read(fd, &be_len, sizeof(be_len));
if (rlen != sizeof(be_len)) {
return FALSE;
}
len1 = ntohl(be_len);
if (len1 >= WEBDAV_MAX_USERNAME_LEN) {
return FALSE;
}
if (len1) {
rlen = read(fd, userName, len1);
if (rlen < 0) {
return FALSE;
}
}
rlen = read(fd, &be_len, sizeof(be_len));
if (rlen != sizeof(be_len)) {
return FALSE;
}
len1 = ntohl(be_len);
if (len1 >= WEBDAV_MAX_USERNAME_LEN) {
return FALSE;
}
if (len1) {
rlen = read(fd, userPassword, len1);
if (rlen < 0) {
return FALSE;
}
}
rlen = read(fd, &be_len, sizeof(be_len));
if (rlen != sizeof(be_len)) {
return FALSE;
}
len1 = ntohl(be_len);
if (len1 >= WEBDAV_MAX_USERNAME_LEN) {
return FALSE;
}
if (len1) {
rlen = read(fd, proxyUserName, len1);
if (rlen < 0) {
return FALSE;
}
}
rlen = read(fd, &be_len, sizeof(be_len));
if (rlen != sizeof(be_len)) {
return FALSE;
}
len1 = ntohl(be_len);
if (len1 >= WEBDAV_MAX_USERNAME_LEN) {
return FALSE;
}
if (len1) {
rlen = read(fd, proxyUserPassword, len1);
if (rlen < 0) {
return FALSE;
}
}
if ( (fd != STDIN_FILENO) &&
(fd != STDOUT_FILENO) &&
(fd != STDERR_FILENO) )
{
struct stat statb;
off_t bytes_to_overwrite;
size_t bytes_to_write;
int zero;
zero = 0;
if (fstat(fd, &statb) != -1) {
bytes_to_overwrite = statb.st_size;
(void)lseek(fd, 0LL, SEEK_SET);
while (bytes_to_overwrite != 0) {
if (bytes_to_overwrite > (off_t)sizeof(zero))
bytes_to_write = sizeof(zero);
else
bytes_to_write = (size_t)bytes_to_overwrite;
if (write(fd, &zero, bytes_to_write) < 0)
{
break;
}
bytes_to_overwrite -= bytes_to_write;
}
(void)fsync(fd);
}
(void)close(fd);
}
return TRUE;
}
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;
int checkKeychain = TRUE;
mntoptparse_t mp;
char user[WEBDAV_MAX_USERNAME_LEN];
char pass[WEBDAV_MAX_PASSWORD_LEN];
char proxy_user[WEBDAV_MAX_USERNAME_LEN];
char proxy_pass[WEBDAV_MAX_PASSWORD_LEN];
struct webdav_request_statfs request_statfs;
struct webdav_reply_statfs reply_statfs;
int tempError;
struct statfs buf;
boolean_t result;
char *errorbuf = NULL;
error = 0;
g_fsid.val[0] = -1;
g_fsid.val[1] = -1;
gProcessUID = getuid();
memset(user, 0, sizeof(user));
memset(pass, 0, sizeof(pass));
memset(proxy_user, 0, sizeof(proxy_user));
memset(proxy_pass, 0, sizeof(proxy_pass));
mntflags = 0;
while ((ch = getopt(argc, argv, "sSa:io:v:")) != -1)
{
switch (ch)
{
case 'a':
{
int fd = atoi(optarg);
checkKeychain = FALSE;
result = readCredentialsFromFile(fd, user, pass,
proxy_user, proxy_pass);
if (result == FALSE) {
syslog(LOG_DEBUG, "%s: readCredentials returned FALSE", __FUNCTION__);
user[0] = '\0';
pass[0] = '\0';
proxy_user[0] = '\0';
proxy_pass[0] = '\0';
}
break;
}
case 'i':
checkKeychain = FALSE;
if (readpassphrase(USER_PROMPT, user, sizeof(user), RPP_REQUIRE_TTY | RPP_ECHO_ON) == NULL) {
user[0] = '\0';
error = errno;
}
else if (readpassphrase(PASS_PROMPT, pass, sizeof(pass), RPP_ECHO_OFF) == NULL) {
pass[0] = '\0';
error = errno;
}
break;
case 'S':
gSuppressAllUI = 1;
break;
case 's':
gSecureServerAuth = TRUE;
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 )
{
strlcpy(volumeName, optarg, NAME_MAX + 1);
}
else
{
error = 1;
}
break;
default:
error = 1;
break;
}
}
if (!error)
{
if (optind != (argc - 2) || strlen(argv[optind]) > MAXPATHLEN)
{
error = 1;
}
}
else if (error == 1) {
error = EINVAL;
}
require_noerr_action_quiet(error, error_exit, usage());
mirrored_mount = gSuppressAllUI && (mntflags & MNT_DONTBROWSE);
require_action(realpath(argv[optind + 1], g_mountPoint) != NULL, error_exit, error = ENOENT);
if ( *volumeName == '\0' )
{
strlcpy(volumeName, strrchr(g_mountPoint, '/') + 1, NAME_MAX + 1);
}
uri = GetMountURI(argv[optind], &gSecureConnection);
require_action_quiet(uri != NULL, error_exit, error = EINVAL);
gUrlIsIdisk = urlIsIdisk(uri);
if (gUrlIsIdisk == TRUE)
gSecureServerAuth = TRUE;
strlcpy(mntfromname, uri , MNAMELEN);
count = getmntinfo(&buffer, MNT_NOWAIT);
mntfromnameLength = (unsigned int)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);
setCacheMaximumSize();
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);
if ( checkKeychain == TRUE ) {
error = get_keychain_credentials(argv[optind], user, sizeof(user), pass, sizeof(pass));
if ( error != 0 )
syslog(LOG_INFO, "%s: get_keychain_credentials exited with result: %d", __FUNCTION__, error);
}
error = authcache_init(user, pass, proxy_user, proxy_pass, NULL);
require_noerr_action_quiet(error, error_exit, error = EINVAL);
cookies_init();
bzero(user, sizeof(user));
bzero(pass, sizeof(pass));
bzero(proxy_user, sizeof(proxy_user));
bzero(proxy_pass, sizeof(proxy_pass));
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);
strlcpy(un.sun_path, TMP_WEBDAV_UDS, sizeof(un.sun_path));
require_action(mktemp(un.sun_path) != NULL, error_exit, error = EINVAL);
mode_mask = umask(0577);
require_action(bind(listen_socket, (struct sockaddr *)&un, (socklen_t)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);
args.pa_mntfromname = mntfromname;
args.pa_version = kCurrentWebdavArgsVersion;
args.pa_socket_namelen = (int)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_server_ident = gServerIdent;
args.pa_root_id = root_node->nodeid;
args.pa_root_fileid = WEBDAV_ROOTFILEID;
args.pa_uid = geteuid();
args.pa_gid = getegid();
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 = (int)_POSIX_CHOWN_RESTRICTED;
args.pa_no_trunc = (int)_POSIX_NO_TRUNC;
bzero(&reply_statfs, sizeof(struct webdav_reply_statfs));
request_statfs.pcr.pcr_uid = getuid();
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 = (uint32_t)reply_statfs.fs_attr.f_bsize;
args.pa_vfsstatfs.f_iosize = (uint32_t)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 = (uint32_t)buf.f_iosize;
return_code = mount(vfc.vfc_name, g_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", g_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;
}
if (sandbox_init("webdav_agent", SANDBOX_NAMED, &errorbuf) == -1) {
syslog(LOG_DEBUG, "sandbox_init: %s\n", errorbuf);
sandbox_free_error(errorbuf);
}
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)
syslog(LOG_ERR, "%s: exiting on select errno %d for %s\n",
__FUNCTION__, error, g_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 )
{
syslog(LOG_ERR, "%s: received signal: %d. Unmounting %s\n",
__FUNCTION__, message, g_mountPoint);
}
else
{
if ( message == -2 )
{
syslog(LOG_DEBUG, "%s: received unmount message for %s\n",
__FUNCTION__, g_mountPoint);
break;
}
else
{
syslog(LOG_ERR, "%s: received message: %d. Force unmounting %s\n",
__FUNCTION__, message, g_mountPoint);
}
}
create_unmount_thread();
}
}
if ( FD_ISSET(listen_socket, &readfds) )
{
struct sockaddr_un addr;
socklen_t addrlen;
addrlen = (socklen_t)sizeof(addr);
accept_socket = accept(listen_socket, (struct sockaddr *)&addr, &addrlen);
if (accept_socket < 0)
{
error = errno;
if (error != EINTR)
syslog(LOG_ERR, "%s: exiting on select errno %d for %s\n",
__FUNCTION__, error, g_mountPoint);
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();
}
}
}
syslog(LOG_DEBUG, "%s unmounted\n", g_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 EAUTH:
break;
case ECANCELED:
break;
case EBUSY:
break;
case ENODEV:
break;
default:
error = EINVAL;
break;
}
}
exit(error);
}