#include #include #include #include "../stringtools.h" #include "../urbackupcommon/os_functions.h" #include #include #include #include #include #include #include #include #ifdef __linux__ #include #endif #include #include extern char **environ; #define DEF_Server #include "../Server.h" #include "../config.h" #ifndef LOOP_SET_DIRECT_IO #define LOOP_SET_DIRECT_IO 0x4C08 #endif #ifndef LOOP_CTL_GET_FREE #define LOOP_CTL_GET_FREE 0x4C82 #endif #ifndef O_CLOEXEC #define O_CLOEXEC 0 #endif #define LOCAL_LO_FLAGS_AUTOCLEAR 4 IServer *Server; const char* mdconfig_path = "/sbin/mdconfig"; const char* umount_path = "/sbin/umount"; std::string getBackupfolderPath() { std::string fn=trim(getFile("/etc/urbackup/backupfolder")); if(fn.find("\n")!=std::string::npos) fn=getuntil("\n", fn); if(fn.find("\r")!=std::string::npos) fn=getuntil("\r", fn); return fn; } std::string handleFilename(std::string fn) { fn=conv_filename(fn); if(fn=="..") { return ""; } return fn; } int exec_wait(const std::string& path, bool keep_stdout, ...) { va_list vl; va_start(vl, keep_stdout); std::vector args; args.push_back(const_cast(path.c_str())); while(true) { const char* p = va_arg(vl, const char*); if(p==NULL) break; args.push_back(const_cast(p)); } va_end(vl); args.push_back(NULL); pid_t child_pid = fork(); if(child_pid==0) { environ = new char*[1]; *environ=NULL; if(!keep_stdout) { int nullfd = open("/dev/null", O_WRONLY); if(nullfd!=-1) { if(dup2(nullfd, 1)==-1) { return -1; } if(dup2(nullfd, 2)==-1) { return -1; } } else { return -1; } } int rc = execvp(path.c_str(), args.data()); exit(rc); } else { int status; waitpid(child_pid, &status, 0); if(WIFEXITED(status)) { return WEXITSTATUS(status); } else { return -1; } } } int exec_wait(const std::string& path, std::string& stdout, ...) { va_list vl; va_start(vl, stdout); std::vector args; args.push_back(const_cast(path.c_str())); while(true) { const char* p = va_arg(vl, const char*); if(p==NULL) break; args.push_back(const_cast(p)); } va_end(vl); args.push_back(NULL); int pipefd[2]; if (pipe(pipefd) == -1) { return -1; } pid_t child_pid = fork(); if(child_pid==0) { environ = new char*[1]; *environ=NULL; close(pipefd[0]); if(dup2(pipefd[1], 1)==-1) { return -1; } if(dup2(pipefd[1], 2)==-1) { return -1; } close(pipefd[1]); int rc = execvp(path.c_str(), args.data()); exit(rc); } else { close(pipefd[1]); char buf[512]; int r; while( (r=read(pipefd[0], buf, 512))>0) { stdout.insert(stdout.end(), buf, buf+r); } close(pipefd[0]); int status; waitpid(child_pid, &status, 0); if(WIFEXITED(status)) { return WEXITSTATUS(status); } else { return -1; } } } std::string find_urbackupsrv_cmd() { static std::string urbackupsrv_cmd; if(!urbackupsrv_cmd.empty()) { return urbackupsrv_cmd; } if(exec_wait(BINDIR "/urbackupsrv", false, "--version", NULL)==1) { urbackupsrv_cmd = BINDIR "/urbackupsrv"; return urbackupsrv_cmd; } else if(exec_wait("/usr/local/bin/urbackupsrv", false, "--version", NULL)==1) { urbackupsrv_cmd="/usr/local/bin/urbackupsrv"; return urbackupsrv_cmd; } if(exec_wait("/sbin/urbackupsrv", false, "--version", NULL)==1) { urbackupsrv_cmd="/sbin/urbackupsrv"; return urbackupsrv_cmd; } else if(exec_wait("/bin/urbackupsrv", false, "--version", NULL)==1) { urbackupsrv_cmd="/bin/urbackupsrv"; return urbackupsrv_cmd; } else if(exec_wait("/usr/sbin/urbackupsrv", false, "--version", NULL)==1) { urbackupsrv_cmd="/usr/sbin/urbackupsrv"; return urbackupsrv_cmd; } else if(exec_wait("/usr/bin/urbackupsrv", false, "--version", NULL)==1) { urbackupsrv_cmd="/usr/bin/urbackupsrv"; return urbackupsrv_cmd; } else { urbackupsrv_cmd="urbackupsrv"; return urbackupsrv_cmd; } } bool chown_dir(const std::string& dir) { passwd* user_info = getpwnam("urbackup"); if(user_info) { int rc = chown(dir.c_str(), user_info->pw_uid, user_info->pw_gid); return rc!=-1; } return false; } bool ubuntu_guestmount_fix() { #ifdef __linux__ //workaround for https://bugs.launchpad.net/ubuntu/+source/linux/+bug/759725 if(getFile("/etc/os-release").find("NAME=\"Ubuntu\"")!=std::string::npos) { std::vector files = getFiles("/boot"); for(size_t i=0;i=0) { mountpoint+=convert(partition); } if(!os_directory_exists(mountpoint) && !os_create_dir(mountpoint)) { std::cerr << "Error creating mountpoint at \"" << mountpoint << "\". Err: " << errno << std::endl; close(loopd); return false; } chown_dir(mountpoint); chown_dir("/dev/loop"+convert(devnum)); passwd* user_info = getpwnam("urbackup"); std::string uid = "uid=0"; std::string gid = "gid=0"; if(user_info) { uid="uid="+convert(user_info->pw_uid); gid="gid="+convert(user_info->pw_gid); } ubuntu_guestmount_fix(); std::cout << "Guestmount..." << std::endl; std::string stdout; bool mount_ok=true; if(exec_wait("guestmount", stdout, "-r", "-n", "--format=raw", "-a", ("/dev/loop"+convert(devnum)).c_str(), "-o", "kernel_cache", "-o", uid.c_str(), "-o", gid.c_str(), "-o", "allow_root", "-m", "/dev/sda", mountpoint.c_str(), NULL) ) { mount_ok=false; std::cout << stdout; size_t qpos = stdout.find("Did you mean to mount one of these filesystems"); if(qpos!=std::string::npos) { stdout=stdout.substr(qpos); stdout=getafter("guestmount:", stdout); while(getbetween("(", ")", stdout)=="swap") { stdout=getafter("guestmount:", stdout); } stdout=trim(getuntil("(", stdout)); std::cout << "Guestmount with dev " << stdout << std::endl; mount_ok=true; if(exec_wait("guestmount", true, "-r", "-n", "--format=raw", "-a", ("/dev/loop"+convert(devnum)).c_str(), "-o", "kernel_cache", "-o", uid.c_str(), "-o", gid.c_str(), "-o", "allow_root", "-m", stdout.c_str(), mountpoint.c_str(), NULL) ) { mount_ok=false; } } if(!mount_ok) { close(loopd); os_remove_dir(mountpoint); return false; } } std::cout << stdout; close(loopd); return true; } #else bool mount_mdconfig(const std::string& imagepath, int partition) { std::string mountpoint = ExtractFilePath(imagepath)+"_mnt"; std::string unitpath = ExtractFilePath(imagepath)+"_unit"; if(partition>=0) { mountpoint+=convert(partition); unitpath+=convert(partition); } if(partition==-1) partition=0; if(os_directory_exists(mountpoint)) { std::cout << "unmounting..." << std::endl; exec_wait(umount_path, true, mountpoint.c_str(), NULL); std::string unit = getFile(unitpath); if(!unit.empty()) { std::cout << "Removing md" << unit << std::endl; exec_wait(mdconfig_path, true, "-d", "-u", unit.c_str(), NULL); } unlink(unitpath.c_str()); } if(!os_directory_exists(mountpoint) && !os_create_dir(mountpoint)) { std::cerr << "Error creating mountpoint at \"" << mountpoint << "\". Err: " << errno << std::endl; return false; } chown_dir(mountpoint); for(size_t i=0;i<1024;++i) { int rc = exec_wait(mdconfig_path, true, "-a", "-t", "vnode", "-f", imagepath.c_str(), "-o", "readonly", "-u", convert(i).c_str(), NULL); if(rc==0) { std::cout << "Found free md unit " << i << std::endl; writestring(convert(i), unitpath.c_str()); std::cout << "Loading kernel module..." << std::endl; exec_wait("/sbin/kldload", true, "fuse.ko", NULL); std::cout << "Mounting /dev/md"+convert(i)+"s"+convert(partition+1)<<" at " << mountpoint << " ..." << std::endl; if(system(("ntfs-3g -o ro /dev/md"+convert(i)+"s"+convert(partition+1)+" \""+ mountpoint+"\"").c_str())==0) { return true; } else { std::cout << "Mounting failed. Removing md device..." << std::endl; exec_wait(mdconfig_path, true, "-d", "-u", convert(i).c_str(), NULL); unlink(unitpath.c_str()); return false; } } } std::cout << "No free mdconfig unit found." << std::endl; return false; } #endif bool mount_image(const std::string& imagepath, int partition, int64 offset, int64 length) { std::string ext = findextension(imagepath); if(ext=="raw") { #ifdef __linux__ return mount_linux_loop(imagepath, partition, offset, length); #else return mount_mdconfig(imagepath, partition); #endif } else { std::string mountpoint = ExtractFilePath(imagepath)+"/contents"; if(partition>=0) { mountpoint+=convert(partition); } if(os_directory_exists(mountpoint) || errno==EACCES || errno==ENOTCONN ) { exec_wait("guestunmount", true, mountpoint.c_str(), NULL); } if(!os_directory_exists(mountpoint) && !os_create_dir(mountpoint)) { std::cerr << "Error creating mountpoint at \"" << mountpoint << "\". Err: " << errno << std::endl; return false; } chown_dir(mountpoint); std::string devpoint = ExtractFilePath(imagepath)+"/device"; if(partition>=0) { devpoint+=convert(partition); } if(os_directory_exists(devpoint) || errno==EACCES || errno==ENOTCONN ) { if(exec_wait("fusermount", true, "-u", devpoint.c_str(), NULL)) { exec_wait("fusermount", true, "-u", "-z", devpoint.c_str(), NULL); } } if(!os_directory_exists(devpoint) && !os_create_dir(devpoint)) { std::cerr << "Error creating devpoint at \"" << devpoint << "\". Err: " << errno << std::endl; os_remove_dir(mountpoint); return false; } chown_dir(devpoint); std::string mount_options=""; passwd* user_info = getpwnam("urbackup"); if(user_info) { mount_options+="uid="+convert(user_info->pw_uid)+",gid="+convert(user_info->pw_gid)+",allow_root"; } ubuntu_guestmount_fix(); if(exec_wait(find_urbackupsrv_cmd(), true, "mount-vhd", "-f", imagepath.c_str(), "-m", mountpoint.c_str(), "-t", devpoint.c_str(), "-o", mount_options.c_str(), "--guestmount", NULL)) { std::cout << "UrBackup mount process returned non-zero return code" << std::endl; os_remove_dir(mountpoint); os_remove_dir(devpoint); return false; } return true; } } bool unmount_image(const std::string& imagepath, int partition) { std::string ext = findextension(imagepath); std::string mountpoint; if(ext=="raw") { mountpoint = ExtractFilePath(imagepath)+"_mnt"; } else { mountpoint = ExtractFilePath(imagepath)+"/contents"; } if(partition>=0) { mountpoint+=convert(partition); } std::cout << "Mountpoint: " << mountpoint << std::endl; bool ret=true; if(os_directory_exists(mountpoint) || errno==EACCES || errno==ENOTCONN) { #ifdef __linux__ std::cout << "Guestunmount..." << std::endl; if(exec_wait("guestunmount", true, mountpoint.c_str(), NULL)) { std::cerr << "Unmounting \"" << mountpoint << "\" failed." << std::endl; ret = false; } #else if(exec_wait(umount_path, true, mountpoint.c_str(), NULL)) { exec_wait(umount_path, true, "-f", mountpoint.c_str(), NULL); std::cerr << "Unmounting \"" << mountpoint << "\" failed." << std::endl; ret = false; } #endif os_remove_dir(mountpoint); } #ifndef __linux__ std::string unitpath = ExtractFilePath(imagepath)+"_unit"; if(partition>=0) { unitpath+=convert(partition); } std::string unit = getFile(unitpath); if(!unit.empty()) { std::cout << "Removing md device " << unit << std::endl; exec_wait(mdconfig_path, true, "-d", "-u", unit.c_str(), NULL); } unlink(unitpath.c_str()); #endif std::string devpoint = ExtractFilePath(imagepath)+"/device"; if(partition>=0) { devpoint+=convert(partition); } if(ext!="raw" && (os_directory_exists(devpoint) || errno==EACCES || errno==ENOTCONN) ) { if(exec_wait("fusermount", true, "-u", devpoint.c_str(), NULL)) { exec_wait("fusermount", true, "-u", "-z", devpoint.c_str(), NULL); exec_wait(umount_path, true, devpoint.c_str(), NULL); } os_remove_dir(devpoint); } return ret; } int main(int argc, char *argv[]) { if(argc<2) { std::cout << "Not enough parameters" << std::endl; return 1; } std::string backupfolder=getBackupfolderPath(); if(backupfolder.empty()) { std::cout << "Backupfolder not set" << std::endl; return 1; } if(seteuid(0)!=0) { std::cout << "Cannot become root user" << std::endl; return 1; } std::string cmd = argv[1]; if(cmd=="mount") { if(argc<5) { std::cout << "Not enough parameters for mount" << std::endl; return 1; } #ifdef __FreeBSD__ //system() on FreeBSD seems to not close file descriptors for (int fd=3; fd<10000; fd++) close(fd); #endif std::string clientname=handleFilename(argv[2]); std::string name=handleFilename(argv[3]); std::string imagename=handleFilename(argv[4]); int partition=-1; int64 offset=-1; int64 length=0; if(argc>7) { partition = watoi(argv[5]); offset=watoi64(argv[6]); length=watoi64(argv[7]); } return mount_image(backupfolder + os_file_sep() + clientname + os_file_sep() + name + os_file_sep() + imagename, partition, offset, length)?0:1; } else if(cmd=="umount") { if(argc<5) { std::cout << "Not enough parameters for umount" << std::endl; return 1; } std::string clientname=handleFilename(argv[2]); std::string name=handleFilename(argv[3]); std::string imagename=handleFilename(argv[4]); int partition=-1; if(argc>5) { partition=watoi(argv[5]); } return unmount_image(backupfolder + os_file_sep() + clientname + os_file_sep() + name + os_file_sep() + imagename, partition)?0:1; } else if(cmd=="test") { #ifndef WITH_FUSEPLUGIN std::cerr << "TEST FAILED: Please compile with mountvhd (./configure --with-mountvhd)" << std::endl; return 1; #endif #if defined(__linux__) if(exec_wait("guestmount", false, "--version", NULL)!=0) { std::cerr << "TEST FAILED: guestmount is missing (libguestfs-tools)" << std::endl; return 1; } #elif defined(__FreeBSD__) if(exec_wait(mdconfig_path, false, "-l", NULL)!=0) { std::cerr << "TEST FAILED: mdconfig not present" << std::endl; return 1; } #else std::cerr << "TEST FAILED: Not FreeBSD or Linux" << std::endl; return 1; #endif std::cout << "MOUNT TEST OK" << std::endl; return 0; } else { std::cout << "Command not found" << std::endl; return 1; } }