/************************************************************************* * UrBackup - Client/Server backup system * Copyright (C) 2011-2016 Martin Raiber * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . **************************************************************************/ #include "os_functions.h" #include "../stringtools.h" #include "server_compat.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __linux__ #include #include #endif #if defined(__FreeBSD__) || defined(__APPLE__) #define lstat64 lstat #define stat64 stat #define statvfs64 statvfs #define open64 open #define readdir64 readdir #define dirent64 dirent #endif void getMousePos(int &x, int &y) { x=0; y=0; } std::vector getFilesWin(const std::string &path, bool *has_error, bool exact_filesize, bool with_usn, bool ignore_other_fs) { return getFiles(path, has_error, ignore_other_fs); } std::vector getFiles(const std::string &path, bool *has_error, bool ignore_other_fs) { if(has_error!=NULL) { *has_error=false; } std::string upath=(path); std::vector tmp; DIR *dp; struct dirent64 *dirp; if((dp = opendir(upath.c_str())) == NULL) { if(has_error!=NULL) { *has_error=true; } std::string errmsg; int err = os_last_error(errmsg); Log("Cannot open \""+path+"\": "+errmsg+" ("+convert(err)+")", LL_ERROR); return tmp; } dev_t parent_dev_id; bool has_parent_dev_id=false; if(ignore_other_fs) { struct stat64 f_info; int rc=lstat64(upath.c_str(), &f_info); if(rc==0) { has_parent_dev_id = true; parent_dev_id = f_info.st_dev; } } upath+=os_file_sep(); errno=0; while ((dirp = readdir64(dp)) != NULL) { SFile f; f.name=(dirp->d_name); if(f.name=="." || f.name==".." ) continue; f.isdir=(dirp->d_type==DT_DIR); struct stat64 f_info; int rc=lstat64((upath+dirp->d_name).c_str(), &f_info); if(rc==0) { f.isdir = S_ISDIR(f_info.st_mode); if(ignore_other_fs && S_ISDIR(f_info.st_mode) && has_parent_dev_id && parent_dev_id!=f_info.st_dev) { continue; } if(S_ISLNK(f_info.st_mode)) { f.issym=true; f.isspecial=true; struct stat64 l_info; int rc2 = stat64((upath+dirp->d_name).c_str(), &l_info); if(rc2==0) { f.isdir=S_ISDIR(l_info.st_mode); } else { f.isdir=false; } } f.usn = (uint64)f_info.st_mtime | ((uint64)f_info.st_ctime<<32); if(f.usn<0) f.usn*=-1; if(!f.isdir) { if(!S_ISREG(f_info.st_mode) ) { f.isspecial=true; } f.size=f_info.st_size; } f.last_modified=f_info.st_mtime; f.created = f_info.st_ctime; f.accessed = f_info.st_atime; } else { std::string errmsg; int err = os_last_error(errmsg); Log("Cannot stat \""+upath+dirp->d_name+"\": "+(errmsg)+" ("+convert(err)+")", LL_ERROR); if(has_error!=NULL) { *has_error=true; } continue; } tmp.push_back(f); errno=0; } if(errno!=0) { std::string errmsg; int err = os_last_error(errmsg); Log("Error listing files in directory \""+path+"\": "+errmsg+" ("+convert(err)+")", LL_ERROR); if(has_error!=NULL) *has_error=true; } closedir(dp); std::sort(tmp.begin(), tmp.end()); return tmp; } void removeFile(const std::string &path) { unlink((path).c_str()); } void moveFile(const std::string &src, const std::string &dst) { rename((src).c_str(), (dst).c_str() ); } bool os_remove_symlink_dir(const std::string &path) { return unlink((path).c_str())==0; } bool os_remove_dir(const std::string &path) { return rmdir(path.c_str())==0; } bool isDirectory(const std::string &path, void* transaction) { struct stat64 f_info; int rc=stat64((path).c_str(), &f_info); if(rc!=0) { rc = lstat64((path).c_str(), &f_info); if(rc!=0) { return false; } } if ( S_ISDIR(f_info.st_mode) ) { return true; } else { return false; } } int os_get_file_type(const std::string &path) { int ret = 0; struct stat64 f_info; int rc1=stat64((path).c_str(), &f_info); if(rc1==0) { if ( S_ISDIR(f_info.st_mode) ) { ret |= EFileType_Directory; } else { ret |= EFileType_File; } } int rc2 = lstat64((path).c_str(), &f_info); if(rc2==0) { if(S_ISLNK(f_info.st_mode)) { ret |= EFileType_Symlink; } if(rc1!=0) { ret |= EFileType_File; } } return ret; } int64 os_atoi64(const std::string &str) { return strtoll(str.c_str(), NULL, 10); } bool os_create_dir(const std::string &path) { return mkdir(path.c_str(), S_IRWXU | S_IRWXG)==0; } bool os_create_reflink(const std::string &linkname, const std::string &fname) { #ifndef sun int src_desc=open64((fname).c_str(), O_RDONLY); if( src_desc<0) { Log("Error opening source file. errno="+convert(errno), LL_INFO); return false; } int dst_desc=open64((linkname).c_str(), O_WRONLY | O_CREAT | O_EXCL, S_IRWXU | S_IRWXG); if( dst_desc<0 ) { Log("Error opening destination file. errno="+convert(errno), LL_INFO); close(src_desc); return false; } #define BTRFS_IOCTL_MAGIC 0x94 #define BTRFS_IOC_CLONE _IOW (BTRFS_IOCTL_MAGIC, 9, int) int rc=ioctl(dst_desc, BTRFS_IOC_CLONE, src_desc); if(rc) { Log("Reflink ioctl failed. errno="+convert(errno), LL_INFO); } close(src_desc); close(dst_desc); if(rc) { if(unlink((linkname).c_str())) { Log("Removing destination file failed. errno="+convert(errno), LL_INFO); } } return rc==0; #else return false; #endif } bool os_create_hardlink(const std::string &linkname, const std::string &fname, bool use_ioref, bool* too_many_links) { if(too_many_links!=NULL) *too_many_links=false; if( use_ioref ) return os_create_reflink(linkname, fname); int rc=link((fname).c_str(), (linkname).c_str()); return rc==0; } int64 os_free_space(const std::string &path) { std::string cp=path; if(path.size()==0) return -1; if(cp[cp.size()-1]=='/') cp.erase(cp.size()-1, 1); if(cp[cp.size()-1]!='/') cp+='/'; struct statvfs64 buf = {}; int rc=statvfs64((path).c_str(), &buf); if(rc==0) { int64 blocksize = buf.f_frsize ? buf.f_frsize : buf.f_bsize; return blocksize*buf.f_bavail; } else { return -1; } } int64 os_total_space(const std::string &path) { std::string cp=path; if(path.size()==0) return -1; if(cp[cp.size()-1]=='/') cp.erase(cp.size()-1, 1); if(cp[cp.size()-1]!='/') cp+='/'; struct statvfs64 buf; int rc=statvfs64((path).c_str(), &buf); if(rc==0) { fsblkcnt_t used=buf.f_blocks-buf.f_bfree; return buf.f_bsize*(used+buf.f_bavail); } else return -1; } bool os_directory_exists(const std::string &path) { //std::string upath=(path); //DIR *dp=opendir(upath.c_str()); //closedir(dp); //return dp!=NULL; return isDirectory(path); } bool os_remove_nonempty_dir(const std::string &path, os_symlink_callback_t symlink_callback, void* userdata, bool delete_root) { std::string upath=(path); if(delete_root) { struct stat64 f_info; int rc = lstat64(upath.c_str(), &f_info); if(rc==0 && S_ISLNK(f_info.st_mode)) { if(unlink(upath.c_str())!=0) { Log("Error deleting symlink \""+upath+"\"", LL_ERROR); } return true; } } std::vector tmp; DIR *dp; struct dirent *dirp; if((dp = opendir(upath.c_str())) == NULL) { Log("No permission to access \""+upath+"\"", LL_ERROR); return false; } bool ok=true; std::vector subdirs; while ((dirp = readdir(dp)) != NULL) { if( (std::string)dirp->d_name!="." && (std::string)dirp->d_name!=".." ) { #ifndef sun if(dirp->d_type==DT_UNKNOWN) { #endif struct stat64 f_info; int rc=lstat64((upath+"/"+(std::string)dirp->d_name).c_str(), &f_info); if(rc==0) { if(S_ISLNK(f_info.st_mode)) { if(symlink_callback!=NULL) { symlink_callback((upath+"/"+(std::string)dirp->d_name), userdata); } else { if(unlink((upath+"/"+(std::string)dirp->d_name).c_str())!=0) { Log("Error deleting symlink \""+upath+"/"+(std::string)dirp->d_name+"\"", LL_ERROR); } } } else if(S_ISDIR(f_info.st_mode) ) { subdirs.push_back((dirp->d_name)); } else { if(unlink((upath+"/"+(std::string)dirp->d_name).c_str())!=0) { Log("Error deleting file \""+upath+"/"+(std::string)dirp->d_name+"\"", LL_ERROR); } } } else { std::string e=convert(errno); switch(errno) { case EACCES: e="EACCES"; break; case EBADF: e="EBADF"; break; case EFAULT: e="EFAULT"; break; case ELOOP: e="ELOOP"; break; case ENAMETOOLONG: e="ENAMETOOLONG"; break; case ENOENT: e="ENOENT"; break; case ENOMEM: e="ENOMEM"; break; case ENOTDIR: e="ENOTDIR"; break; } Log("No permission to stat \""+upath+"/"+dirp->d_name+"\" error: "+e, LL_ERROR); } #ifndef sun } else if(dirp->d_type==DT_DIR ) { subdirs.push_back((dirp->d_name)); } else if(dirp->d_type==DT_LNK ) { if(symlink_callback!=NULL) { symlink_callback((upath+"/"+(std::string)dirp->d_name), userdata); } else { if(unlink((upath+"/"+(std::string)dirp->d_name).c_str())!=0) { Log("Error deleting symlink \""+upath+"/"+(std::string)dirp->d_name+"\"", LL_ERROR); } } } else { if(unlink((upath+"/"+(std::string)dirp->d_name).c_str())!=0) { Log("Error deleting file \""+upath+"/"+(std::string)dirp->d_name+"\"", LL_ERROR); } } #endif } } closedir(dp); for(size_t i=0;iai_addrlen>=sizeof(sockaddr_in)) { *dest=reinterpret_cast(h->ai_addr)->sin_addr.s_addr; freeaddrinfo(h); return true; } else { freeaddrinfo(h); return false; } } else { return false; } } else { return false; } } return true; } std::string os_file_prefix(std::string path) { return path; } bool os_file_truncate(const std::string &fn, int64 fsize) { if( truncate((fn).c_str(), (off_t)fsize) !=0 ) { return false; } return true; } std::string os_strftime(std::string fs) { time_t rawtime; char buffer [100]; time ( &rawtime ); struct tm *timeinfo; timeinfo = localtime ( &rawtime ); strftime (buffer,100,fs.c_str(),timeinfo); std::string r(buffer); return r; } bool os_create_dir_recursive(std::string fn) { if(fn.empty()) return false; bool b=os_create_dir(fn); if(!b) { b=os_create_dir_recursive(ExtractFilePath(fn)); if(!b) return false; return os_create_dir(fn); } else { return true; } } bool os_rename_file(std::string src, std::string dst, void* transaction) { int rc=rename((src).c_str(), (dst).c_str()); return rc==0; } bool os_get_symlink_target(const std::string &lname, std::string &target) { std::string lname_utf8 = (lname); struct stat sb; if(lstat(lname_utf8.c_str(), &sb)==-1) { return false; } std::string target_buf; target_buf.resize(sb.st_size); ssize_t rc = readlink(lname_utf8.c_str(), &target_buf[0], sb.st_size); if(rc<0) { return false; } if(rc > sb.st_size) { return false; } else if(rc(accessed); time_t mtime = static_cast(last_modified); #ifndef __APPLE__ timespec tss[2]; tss[0].tv_sec = atime; tss[0].tv_nsec = 0; tss[1].tv_sec = mtime; tss[1].tv_nsec = 0; int rc = utimensat(0, fn.c_str(), tss, AT_SYMLINK_NOFOLLOW); return rc==0; #else int fd = open(fn.c_str(), O_WRONLY|O_NOFOLLOW|O_SYMLINK|O_CLOEXEC); if(fd==-1) { return false; } struct timeval tv[2]; tv[0].tv_sec = atime; tv[0].tv_usec = 0; tv[1].tv_sec = mtime; tv[1].tv_usec = 0; int rc = futimes(fd, tv); close(fd); return rc==0; #endif } #ifndef OS_FUNC_NO_SERVER bool copy_file(const std::string &src, const std::string &dst, bool flush) { IFile *fsrc=Server->openFile(src, MODE_READ); if(fsrc==NULL) return false; IFile *fdst=Server->openFile(dst, MODE_WRITE); if(fdst==NULL) { Server->destroy(fsrc); return false; } bool copy_ok = copy_file(fsrc, fdst); if (copy_ok && flush) { copy_ok = fdst->Sync(); } Server->destroy(fsrc); Server->destroy(fdst); return copy_ok; } bool copy_file(IFile *fsrc, IFile *fdst) { if(fsrc==NULL || fdst==NULL) { return false; } if(!fsrc->Seek(0)) { return false; } if(!fdst->Seek(0)) { return false; } char buf[4096]; size_t rc; bool has_error=false; while( (rc=(_u32)fsrc->Read(buf, 4096, &has_error))>0) { if(rc>0) { fdst->Write(buf, (_u32)rc, &has_error); if(has_error) { break; } } } if(has_error) { return false; } else { return true; } } #endif //OS_FUNC_NO_SERVER SFile getFileMetadataWin( const std::string &path, bool with_usn) { return getFileMetadata(path); } SFile getFileMetadata( const std::string &path ) { SFile ret; ret.name=path; struct stat64 f_info; int rc=lstat64((path).c_str(), &f_info); if(rc==0) { if(S_ISDIR(f_info.st_mode) ) { ret.isdir = true; } ret.size = f_info.st_size; ret.last_modified = f_info.st_mtime; ret.created = f_info.st_ctime; ret.accessed = f_info.st_atime; return ret; } else { return SFile(); } } std::string os_get_final_path(std::string path) { char* retptr = realpath((path).c_str(), NULL); if(retptr==NULL) { return path; } std::string ret = (retptr); free(retptr); return ret; } bool os_path_absolute(const std::string& path) { if(!path.empty() && path[0]=='/') { return true; } else { return false; } } int os_popen(const std::string& cmd, std::string& ret) { ret.clear(); FILE* in = NULL; #ifndef _WIN32 #define _popen popen #define _pclose pclose #endif in = _popen(cmd.c_str(), "r"); if(in==NULL) { return -1; } char buf[4096]; size_t read; do { read=fread(buf, 1, sizeof(buf), in); if(read>0) { ret.append(buf, buf+read); } } while(read==sizeof(buf)); return _pclose(in); } #ifdef __linux__ #if defined(__i386__) #define __NR_ioprio_set 289 #define __NR_ioprio_get 290 #elif defined(__ppc__) #define __NR_ioprio_set 273 #define __NR_ioprio_get 274 #elif defined(__x86_64__) #define __NR_ioprio_set 251 #define __NR_ioprio_get 252 #elif defined(__ia64__) #define __NR_ioprio_set 1274 #define __NR_ioprio_get 1275 #endif #endif //__linux__ #ifdef __NR_ioprio_set static inline int ioprio_set(int which, int who, int ioprio) { return syscall(__NR_ioprio_set, which, who, ioprio); } static inline int ioprio_get(int which, int who) { return syscall(__NR_ioprio_get, which, who); } enum { IOPRIO_CLASS_NONE, IOPRIO_CLASS_RT, IOPRIO_CLASS_BE, IOPRIO_CLASS_IDLE, }; enum { IOPRIO_WHO_PROCESS = 1, IOPRIO_WHO_PGRP, IOPRIO_WHO_USER, }; #define IOPRIO_CLASS_SHIFT 13 struct SPrioInfoInt { int io_prio; int cpu_prio; }; SPrioInfo::SPrioInfo() : prio_info(new SPrioInfoInt) { } SPrioInfo::~SPrioInfo() { delete prio_info; } pid_t gettid() { return (pid_t) syscall (SYS_gettid); } bool os_enable_background_priority(SPrioInfo& prio_info) { pid_t tid = gettid(); prio_info.prio_info->io_prio = ioprio_get(IOPRIO_WHO_PROCESS, tid); prio_info.prio_info->cpu_prio = getpriority(IOPRIO_WHO_PROCESS, tid); int ioprio = 7; int ioprio_class = IOPRIO_CLASS_IDLE; if(ioprio_set(IOPRIO_WHO_PROCESS, tid, ioprio | ioprio_class << IOPRIO_CLASS_SHIFT)==-1) { return false; } int cpuprio = 19; if(setpriority(PRIO_PROCESS, tid, cpuprio)==-1) { return false; } return true; } bool os_disable_background_priority(SPrioInfo& prio_info) { if(prio_info.prio_info==NULL) { return false; } pid_t tid = gettid(); if(ioprio_set(IOPRIO_WHO_PROCESS, tid, prio_info.prio_info->io_prio)==-1) { return false; } if(setpriority(PRIO_PROCESS, tid, prio_info.prio_info->cpu_prio)==-1) { return false; } return true; } #else //__NR_ioprio_set SPrioInfo::SPrioInfo() { } SPrioInfo::~SPrioInfo() { } bool os_enable_background_priority(SPrioInfo& prio_info) { return false; } bool os_disable_background_priority(SPrioInfo& prio_info) { return false; } #endif //__NR_ioprio_set