int main(int argc, char **argv) { int reti; uint16_t vid; struct vol *vol; struct dir *retdir; struct path *path; /* initialize */ printf("Initializing\n============\n"); TEST(setuplog("default:note","/dev/tty")); TEST( afp_options_parse_cmdline(&obj, 3, &args[0]) ); TEST_int( afp_config_parse(&obj, NULL), 0); TEST_int( configinit(&obj), 0); TEST( cnid_init() ); TEST( load_volumes(&obj, NULL) ); TEST_int( dircache_init(8192), 0); obj.afp_version = 32; printf("\n"); /* now run tests */ printf("Running tests\n=============\n"); TEST_expr(vid = openvol(&obj, "test"), vid != 0); TEST_expr(vol = getvolbyvid(vid), vol != NULL); /* test directory.c stuff */ TEST_expr(retdir = dirlookup(vol, DIRDID_ROOT_PARENT), retdir != NULL); TEST_expr(retdir = dirlookup(vol, DIRDID_ROOT), retdir != NULL); TEST_expr(path = cname(vol, retdir, cnamewrap("Network Trash Folder")), path != NULL); TEST_expr(retdir = dirlookup(vol, DIRDID_ROOT), retdir != NULL); TEST_int(getfiledirparms(&obj, vid, DIRDID_ROOT_PARENT, "test"), 0); TEST_int(getfiledirparms(&obj, vid, DIRDID_ROOT, ""), 0); TEST_expr(reti = createdir(&obj, vid, DIRDID_ROOT, "dir1"), reti == 0 || reti == AFPERR_EXIST); TEST_int(getfiledirparms(&obj, vid, DIRDID_ROOT, "dir1"), 0); /* FIXME: this doesn't work although it should. "//" get translated to \000 \000 at means ".." ie this should getfiledirparms for DIRDID_ROOT_PARENT -- at least afair! TEST_int(getfiledirparms(&configs->obj, vid, DIRDID_ROOT, "//"), 0); */ TEST_int(createfile(&obj, vid, DIRDID_ROOT, "dir1/file1"), 0); TEST_int(delete(&obj, vid, DIRDID_ROOT, "dir1/file1"), 0); TEST_int(delete(&obj, vid, DIRDID_ROOT, "dir1"), 0); TEST_int(createfile(&obj, vid, DIRDID_ROOT, "file1"), 0); TEST_int(getfiledirparms(&obj, vid, DIRDID_ROOT, "file1"), 0); TEST_int(delete(&obj, vid, DIRDID_ROOT, "file1"), 0); /* test enumerate.c stuff */ TEST_int(enumerate(&obj, vid, DIRDID_ROOT), 0); }
int main(int argc, char **argv) { AFPObj obj = { 0 }; if (argc < 2) { usage_main(); return 1; } if (afp_config_parse(&obj, "ad") != 0) return 1; setuplog("default:note", "/dev/tty"); if (load_volumes(&obj, LV_DEFAULT) != 0) return 1; if (STRCMP(argv[1], ==, "ls")) return ad_ls(argc - 1, argv + 1, &obj); else if (STRCMP(argv[1], ==, "cp"))
int main(int argc, char **argv) { int c, lockfd, ret = -1; int dump=0, scan=0, rebuild=0, prep_upgrade=0, rebuildindexes=0, dumpindexes=0, force=0; dbd_flags_t flags = 0; char *volpath; int cdir; AFPObj obj = { 0 }; struct vol *vol; if (geteuid() != 0) { usage(); exit(EXIT_FAILURE); } /* Inhereting perms in ad_mkdir etc requires this */ ad_setfuid(0); while ((c = getopt(argc, argv, ":cCdefFinrstuvx")) != -1) { switch(c) { case 'c': flags |= DBD_FLAGS_CLEANUP; break; case 'C': flags |= DBD_FLAGS_V2TOEA; break; case 'd': dump = 1; break; case 'i': dumpindexes = 1; break; case 's': scan = 1; flags |= DBD_FLAGS_SCAN; break; case 'n': nocniddb = 1; /* FIXME: this could/should be a flag too for consistency */ break; case 'r': rebuild = 1; break; case 't': flags |= DBD_FLAGS_STATS; break; case 'u': prep_upgrade = 1; break; case 'v': verbose = 1; break; case 'e': exclusive = 1; flags |= DBD_FLAGS_EXCL; break; case 'x': rebuildindexes = 1; break; case 'f': force = 1; exclusive = 1; flags |= DBD_FLAGS_FORCE | DBD_FLAGS_EXCL; break; case 'F': obj.cmdlineconfigfile = strdup(optarg); break; case ':': case '?': usage(); exit(EXIT_FAILURE); break; } } if ((dump + scan + rebuild + prep_upgrade) != 1) { usage(); exit(EXIT_FAILURE); } if ( (optind + 1) != argc ) { usage(); exit(EXIT_FAILURE); } volpath = argv[optind]; setvbuf(stdout, (char *) NULL, _IONBF, 0); /* Remember cwd */ if ((cdir = open(".", O_RDONLY)) < 0) { dbd_log( LOGSTD, "Can't open dir: %s", strerror(errno)); exit(EXIT_FAILURE); } /* Setup signal handling */ set_signal(); /* Setup logging. Should be portable among *NIXes */ if (!verbose) setuplog("default:info", "/dev/tty"); else setuplog("default:debug", "/dev/tty"); /* Load config */ if (afp_config_parse(&obj, "dbd") != 0) { dbd_log( LOGSTD, "Couldn't load afp.conf"); exit(EXIT_FAILURE); } if (load_volumes(&obj, NULL) != 0) { dbd_log( LOGSTD, "Couldn't load volumes"); exit(EXIT_FAILURE); } if ((vol = getvolbypath(&obj, volpath)) == NULL) { dbd_log( LOGSTD, "Couldn't find volume for '%s'", volpath); exit(EXIT_FAILURE); } if (load_charset(vol) != 0) { dbd_log( LOGSTD, "Couldn't load charsets for '%s'", volpath); exit(EXIT_FAILURE); } pack_setvol(vol); if (vol->v_adouble == AD_VERSION_EA) dbd_log( LOGDEBUG, "adouble:ea volume"); else if (vol->v_adouble == AD_VERSION2) dbd_log( LOGDEBUG, "adouble:v2 volume"); else { dbd_log( LOGSTD, "unknown adouble volume"); exit(EXIT_FAILURE); } /* -C v2 to ea conversion only on adouble:ea volumes */ if ((flags & DBD_FLAGS_V2TOEA) && (vol->v_adouble!= AD_VERSION_EA)) { dbd_log( LOGSTD, "Can't run adouble:v2 to adouble:ea conversion because not an adouble:ea volume"); exit(EXIT_FAILURE); } /* Sanity checks to ensure we can touch this volume */ if (vol->v_vfs_ea != AFPVOL_EA_AD && vol->v_vfs_ea != AFPVOL_EA_SYS) { dbd_log( LOGSTD, "Unknown Extended Attributes option: %u", vol->v_vfs_ea); exit(EXIT_FAILURE); } /* Enuser dbpath is there, create if necessary */ struct stat st; if (stat(vol->v_dbpath, &st) != 0) { if (errno != ENOENT) { dbd_log( LOGSTD, "Can't stat dbpath \"%s\": %s", vol->v_dbpath, strerror(errno)); exit(EXIT_FAILURE); } if ((mkdir(vol->v_dbpath, 0755)) != 0) { dbd_log( LOGSTD, "Can't create dbpath \"%s\": %s", vol->v_dbpath, strerror(errno)); exit(EXIT_FAILURE); } } /* Put "/.AppleDB" at end of volpath, get path from volinfo file */ if ( (strlen(vol->v_dbpath) + strlen("/.AppleDB")) > MAXPATHLEN ) { dbd_log( LOGSTD, "Volume pathname too long"); exit(EXIT_FAILURE); } strncpy(dbpath, vol->v_dbpath, MAXPATHLEN - strlen("/.AppleDB")); strcat(dbpath, "/.AppleDB"); /* Check or create dbpath */ int dbdirfd = open(dbpath, O_RDONLY); if (dbdirfd == -1 && errno == ENOENT) { if (errno == ENOENT) { if ((mkdir(dbpath, 0755)) != 0) { dbd_log( LOGSTD, "Can't create .AppleDB for \"%s\": %s", dbpath, strerror(errno)); exit(EXIT_FAILURE); } } else { dbd_log( LOGSTD, "Somethings wrong with .AppleDB for \"%s\", giving up: %s", dbpath, strerror(errno)); exit(EXIT_FAILURE); } } else { close(dbdirfd); } /* Get db lock */ if ((db_locked = get_lock(LOCK_EXCL, dbpath)) == -1) goto exit_noenv; if (db_locked != LOCK_EXCL) { /* Couldn't get exclusive lock, try shared lock if -e wasn't requested */ if (exclusive) { dbd_log(LOGSTD, "Database is in use and exlusive was requested"); goto exit_noenv; } if ((db_locked = get_lock(LOCK_SHRD, NULL)) != LOCK_SHRD) goto exit_noenv; } /* Check if -f is requested and wipe db if yes */ if ((flags & DBD_FLAGS_FORCE) && rebuild) { char cmd[8 + MAXPATHLEN]; if ((db_locked = get_lock(LOCK_FREE, NULL)) != 0) goto exit_noenv; snprintf(cmd, 8 + MAXPATHLEN, "rm -rf \"%s\"", dbpath); dbd_log( LOGDEBUG, "Removing old database of volume: '%s'", volpath); system(cmd); if ((mkdir(dbpath, 0755)) != 0) { dbd_log( LOGSTD, "Can't create dbpath \"%s\": %s", dbpath, strerror(errno)); exit(EXIT_FAILURE); } dbd_log( LOGDEBUG, "Removed old database."); if ((db_locked = get_lock(LOCK_EXCL, dbpath)) == -1) goto exit_noenv; } /* Lets start with the BerkeleyDB stuff */ if ( ! nocniddb) { if ((dbd = dbif_init(dbpath, "cnid2.db")) == NULL) goto exit_noenv; if (dbif_env_open(dbd, &db_param, (db_locked == LOCK_EXCL) ? (DBOPTIONS | DB_RECOVER) : DBOPTIONS) < 0) { dbd_log( LOGSTD, "error opening database!"); goto exit_noenv; } if (db_locked == LOCK_EXCL) dbd_log( LOGDEBUG, "Finished recovery."); if (dbif_open(dbd, NULL, rebuildindexes) < 0) { dbif_close(dbd); goto exit_failure; } /* Prepare upgrade ? We're done */ if (prep_upgrade) { (void)dbif_txn_close(dbd, 1); goto cleanup; } } /* Downgrade db lock if not running exclusive */ if (!exclusive && (db_locked == LOCK_EXCL)) { if (get_lock(LOCK_UNLOCK, NULL) != 0) goto exit_failure; if (get_lock(LOCK_SHRD, NULL) != LOCK_SHRD) goto exit_failure; } /* Now execute given command scan|rebuild|dump */ if (dump && ! nocniddb) { if (dbif_dump(dbd, dumpindexes) < 0) { dbd_log( LOGSTD, "Error dumping database"); } } else if ((rebuild && ! nocniddb) || scan) { if (cmd_dbd_scanvol(dbd, vol, flags) < 0) { dbd_log( LOGSTD, "Error repairing database."); } } cleanup: /* Cleanup */ dbd_log(LOGDEBUG, "Closing db"); if (! nocniddb) { if (dbif_close(dbd) < 0) { dbd_log( LOGSTD, "Error closing database"); goto exit_failure; } } exit_success: ret = 0; exit_failure: if (dbif_env_remove(dbpath) < 0) { dbd_log( LOGSTD, "Error removing BerkeleyDB database environment"); ret++; } get_lock(0, NULL); exit_noenv: if ((fchdir(cdir)) < 0) dbd_log(LOGSTD, "fchdir: %s", strerror(errno)); if (ret == 0) exit(EXIT_SUCCESS); else exit(EXIT_FAILURE); }
int main(int ac, char **av) { fd_set rfds; void *ipc; struct sigaction sv; sigset_t sigs; int ret; /* Parse argv args and initialize default options */ afp_options_parse_cmdline(&obj, ac, av); if (!(obj.cmdlineflags & OPTION_DEBUG) && (daemonize(0, 0) != 0)) exit(EXITERR_SYS); /* Log SIGBUS/SIGSEGV SBT */ fault_setup(NULL); if (afp_config_parse(&obj, "afpd") != 0) afp_exit(EXITERR_CONF); /* Save the user's current umask */ obj.options.save_mask = umask(obj.options.umask); /* install child handler for asp and dsi. we do this before afp_goaway * as afp_goaway references stuff from here. * XXX: this should really be setup after the initial connections. */ if (!(server_children = server_child_alloc(obj.options.connections, CHILD_NFORKS))) { LOG(log_error, logtype_afpd, "main: server_child alloc: %s", strerror(errno) ); afp_exit(EXITERR_SYS); } sigemptyset(&sigs); pthread_sigmask(SIG_SETMASK, &sigs, NULL); memset(&sv, 0, sizeof(sv)); /* linux at least up to 2.4.22 send a SIGXFZ for vfat fs, even if the file is open with O_LARGEFILE ! */ #ifdef SIGXFSZ sv.sa_handler = SIG_IGN; sigemptyset( &sv.sa_mask ); if (sigaction(SIGXFSZ, &sv, NULL ) < 0 ) { LOG(log_error, logtype_afpd, "main: sigaction: %s", strerror(errno) ); afp_exit(EXITERR_SYS); } #endif sv.sa_handler = afp_goaway; /* handler for all sigs */ sigemptyset( &sv.sa_mask ); sigaddset(&sv.sa_mask, SIGALRM); sigaddset(&sv.sa_mask, SIGHUP); sigaddset(&sv.sa_mask, SIGTERM); sigaddset(&sv.sa_mask, SIGUSR1); sigaddset(&sv.sa_mask, SIGQUIT); sv.sa_flags = SA_RESTART; if ( sigaction( SIGCHLD, &sv, NULL ) < 0 ) { LOG(log_error, logtype_afpd, "main: sigaction: %s", strerror(errno) ); afp_exit(EXITERR_SYS); } sigemptyset( &sv.sa_mask ); sigaddset(&sv.sa_mask, SIGALRM); sigaddset(&sv.sa_mask, SIGTERM); sigaddset(&sv.sa_mask, SIGHUP); sigaddset(&sv.sa_mask, SIGCHLD); sigaddset(&sv.sa_mask, SIGQUIT); sv.sa_flags = SA_RESTART; if ( sigaction( SIGUSR1, &sv, NULL ) < 0 ) { LOG(log_error, logtype_afpd, "main: sigaction: %s", strerror(errno) ); afp_exit(EXITERR_SYS); } sigemptyset( &sv.sa_mask ); sigaddset(&sv.sa_mask, SIGALRM); sigaddset(&sv.sa_mask, SIGTERM); sigaddset(&sv.sa_mask, SIGUSR1); sigaddset(&sv.sa_mask, SIGCHLD); sigaddset(&sv.sa_mask, SIGQUIT); sv.sa_flags = SA_RESTART; if ( sigaction( SIGHUP, &sv, NULL ) < 0 ) { LOG(log_error, logtype_afpd, "main: sigaction: %s", strerror(errno) ); afp_exit(EXITERR_SYS); } sigemptyset( &sv.sa_mask ); sigaddset(&sv.sa_mask, SIGALRM); sigaddset(&sv.sa_mask, SIGHUP); sigaddset(&sv.sa_mask, SIGUSR1); sigaddset(&sv.sa_mask, SIGCHLD); sigaddset(&sv.sa_mask, SIGQUIT); sv.sa_flags = SA_RESTART; if ( sigaction( SIGTERM, &sv, NULL ) < 0 ) { LOG(log_error, logtype_afpd, "main: sigaction: %s", strerror(errno) ); afp_exit(EXITERR_SYS); } sigemptyset( &sv.sa_mask ); sigaddset(&sv.sa_mask, SIGALRM); sigaddset(&sv.sa_mask, SIGHUP); sigaddset(&sv.sa_mask, SIGUSR1); sigaddset(&sv.sa_mask, SIGCHLD); sigaddset(&sv.sa_mask, SIGTERM); sv.sa_flags = SA_RESTART; if (sigaction(SIGQUIT, &sv, NULL ) < 0 ) { LOG(log_error, logtype_afpd, "main: sigaction: %s", strerror(errno) ); afp_exit(EXITERR_SYS); } /* afp.conf: not in config file: lockfile, configfile * preference: command-line provides defaults. * config file over-writes defaults. * * we also need to make sure that killing afpd during startup * won't leave any lingering registered names around. */ sigemptyset(&sigs); sigaddset(&sigs, SIGALRM); sigaddset(&sigs, SIGHUP); sigaddset(&sigs, SIGUSR1); #if 0 /* don't block SIGTERM */ sigaddset(&sigs, SIGTERM); #endif sigaddset(&sigs, SIGCHLD); pthread_sigmask(SIG_BLOCK, &sigs, NULL); if (configinit(&obj) != 0) { LOG(log_error, logtype_afpd, "main: no servers configured"); afp_exit(EXITERR_CONF); } pthread_sigmask(SIG_UNBLOCK, &sigs, NULL); /* Initialize */ cnid_init(); /* watch atp, dsi sockets and ipc parent/child file descriptor. */ if (obj.options.flags & OPTION_KEEPSESSIONS) { LOG(log_note, logtype_afpd, "Activating continous service"); disasociated_ipc_fd = ipc_server_uds(_PATH_AFP_IPC); } fd_set_listening_sockets(&obj); /* set limits */ (void)setlimits(); afp_child_t *child; int recon_ipc_fd; pid_t pid; int saveerrno; /* wait for an appleshare connection. parent remains in the loop * while the children get handled by afp_over_{asp,dsi}. this is * currently vulnerable to a denial-of-service attack if a * connection is made without an actual login attempt being made * afterwards. establishing timeouts for logins is a possible * solution. */ while (1) { LOG(log_maxdebug, logtype_afpd, "main: polling %i fds", fdset_used); pthread_sigmask(SIG_UNBLOCK, &sigs, NULL); ret = poll(fdset, fdset_used, -1); pthread_sigmask(SIG_BLOCK, &sigs, NULL); saveerrno = errno; if (gotsigchld) { gotsigchld = 0; child_handler(); continue; } if (reloadconfig) { nologin++; auth_unload(); fd_reset_listening_sockets(&obj); LOG(log_info, logtype_afpd, "re-reading configuration file"); configfree(&obj, NULL); if (configinit(&obj) != 0) { LOG(log_error, logtype_afpd, "config re-read: no servers configured"); afp_exit(EXITERR_CONF); } fd_set_listening_sockets(&obj); nologin = 0; reloadconfig = 0; errno = saveerrno; continue; } if (ret == 0) continue; if (ret < 0) { if (errno == EINTR) continue; LOG(log_error, logtype_afpd, "main: can't wait for input: %s", strerror(errno)); break; } for (int i = 0; i < fdset_used; i++) { if (fdset[i].revents & (POLLIN | POLLERR | POLLHUP | POLLNVAL)) { switch (polldata[i].fdtype) { case LISTEN_FD: if (child = dsi_start(&obj, (DSI *)polldata[i].data, server_children)) { /* Add IPC fd to select fd set */ fdset_add_fd(obj.options.connections + AFP_LISTENERS + FDSET_SAFETY, &fdset, &polldata, &fdset_used, &fdset_size, child->ipc_fd, IPC_FD, child); } break; case IPC_FD: child = (afp_child_t *)polldata[i].data; LOG(log_debug, logtype_afpd, "main: IPC request from child[%u]", child->pid); if (ipc_server_read(server_children, child->ipc_fd) != 0) { fdset_del_fd(&fdset, &polldata, &fdset_used, &fdset_size, child->ipc_fd); close(child->ipc_fd); child->ipc_fd = -1; if ((obj.options.flags & OPTION_KEEPSESSIONS) && child->disasociated) { LOG(log_note, logtype_afpd, "main: removing reattached child[%u]", child->pid); server_child_remove(server_children, CHILD_DSIFORK, child->pid); } } break; case DISASOCIATED_IPC_FD: LOG(log_debug, logtype_afpd, "main: IPC reconnect request"); if ((recon_ipc_fd = accept(disasociated_ipc_fd, NULL, NULL)) == -1) { LOG(log_error, logtype_afpd, "main: accept: %s", strerror(errno)); break; } if (readt(recon_ipc_fd, &pid, sizeof(pid_t), 0, 1) != sizeof(pid_t)) { LOG(log_error, logtype_afpd, "main: readt: %s", strerror(errno)); close(recon_ipc_fd); break; } LOG(log_note, logtype_afpd, "main: IPC reconnect from pid [%u]", pid); if ((child = server_child_add(server_children, CHILD_DSIFORK, pid, recon_ipc_fd)) == NULL) { LOG(log_error, logtype_afpd, "main: server_child_add"); close(recon_ipc_fd); break; } child->disasociated = 1; fdset_add_fd(obj.options.connections + AFP_LISTENERS + FDSET_SAFETY, &fdset, &polldata, &fdset_used, &fdset_size, recon_ipc_fd, IPC_FD, child); break; default: LOG(log_debug, logtype_afpd, "main: IPC request for unknown type"); break; } /* switch */ } /* if */ } /* for (i)*/ } /* while (1) */ return 0; }