Loading commands/system/cpio.cpp +162 −2 Changes for commands/system/cpio.cpp: 162 added lines, 2 removed lines. Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include <iostream> #include <string> #include <vector> #include <cstdio> #include <cstring> #include <cerrno> Loading @@ -37,6 +38,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include <sys/stat.h> #include <sys/sysmacros.h> #include "../filesystem/copyutil.h" #include "shell.h" namespace fresh { namespace cmd { Loading Loading @@ -151,11 +153,151 @@ namespace fresh { namespace cmd { return true; } static bool normalizeRelativePath(const std::string &path,std::string &relative){ std::vector<std::string> components; size_t start=0; while(start<path.size()){ size_t slash=path.find('/',start); std::string component=path.substr(start,slash==std::string::npos ? std::string::npos : slash-start); if(component==".."){ if(components.empty()) return false; components.pop_back(); }else if(!component.empty() && component!="."){ components.push_back(component); } if(slash==std::string::npos) break; start=slash+1; } relative.clear(); for(const auto &component:components){ if(!relative.empty()) relative+='/'; relative+=component; } return true; } static int copyThrough(const std::string &destination,bool createDirs, bool unconditional,bool preserveTimes,char delimiter){ struct stat destinationStat; if(stat(destination.c_str(),&destinationStat)!=0 || !S_ISDIR(destinationStat.st_mode)){ std::cerr << "cpio: " << destination << ": not a directory" << std::endl; return 1; } copy::Options copyOptions; copyOptions.prog="cpio"; copyOptions.deref=copy::Deref::Never; copyOptions.recursive=true; copyOptions.update=!unconditional; copyOptions.keepTimes=preserveTimes; copy::Copier copier(copyOptions); std::string root=destination; while(root.size()>1 && root.back()=='/') root.pop_back(); bool ok=true; std::vector<std::pair<std::string,struct stat>> directories; std::string source; while(std::getline(std::cin,source,delimiter)){ if(source.empty()) continue; std::string relative; if(!normalizeRelativePath(source,relative)){ std::cerr << "cpio: refusing path outside destination: " << source << std::endl; ok=false; continue; } struct stat sourceStat; if(lstat(source.c_str(),&sourceStat)!=0){ std::cerr << "cpio: " << source << ": " << strerror(errno) << std::endl; ok=false; continue; } std::string target=root; size_t componentStart=0; std::vector<std::string> components; while(componentStart<relative.size()){ size_t slash=relative.find('/',componentStart); components.push_back(relative.substr(componentStart,slash==std::string::npos ? std::string::npos : slash-componentStart)); if(slash==std::string::npos) break; componentStart=slash+1; } size_t directoryCount=S_ISDIR(sourceStat.st_mode) ? components.size() : (components.empty() ? 0 : components.size()-1); bool pathReady=true; for(size_t i=0;i<directoryCount;++i){ target+="/"+components[i]; struct stat targetStat; if(lstat(target.c_str(),&targetStat)==0){ if(!S_ISDIR(targetStat.st_mode)){ std::cerr << "cpio: " << target << ": not a directory" << std::endl; pathReady=false; break; } }else if(errno==ENOENT && createDirs){ mode_t mode=(i+1==components.size() && S_ISDIR(sourceStat.st_mode)) ? sourceStat.st_mode&0777 : 0755; if(mkdir(target.c_str(),mode|S_IRWXU)!=0){ std::cerr << "cpio: cannot create directory " << target << ": " << strerror(errno) << std::endl; pathReady=false; break; } }else{ std::cerr << "cpio: cannot access directory " << target << ": " << strerror(errno) << std::endl; pathReady=false; break; } } if(!pathReady){ ok=false; continue; } if(!relative.empty()) target+="/"+components.back(); if(S_ISDIR(sourceStat.st_mode)){ if(!relative.empty()){ struct stat targetStat; if(lstat(target.c_str(),&targetStat)!=0){ if(errno!=ENOENT || !createDirs || mkdir(target.c_str(),(sourceStat.st_mode&0777)|S_IRWXU)!=0){ std::cerr << "cpio: cannot create directory " << target << ": " << strerror(errno) << std::endl; ok=false; continue; } }else if(!S_ISDIR(targetStat.st_mode)){ std::cerr << "cpio: " << target << ": not a directory" << std::endl; ok=false; continue; } } if(preserveTimes) directories.push_back({target,sourceStat}); continue; } if(!copier.copy(source,target,true)) ok=false; } if(std::cin.bad()){ std::cerr << "cpio: error reading pathname list" << std::endl; ok=false; } if(preserveTimes){ for(auto it=directories.rbegin();it!=directories.rend();++it){ struct timespec times[2]={it->second.st_atim,it->second.st_mtim}; if(utimensat(AT_FDCWD,it->first.c_str(),times,0)!=0){ std::cerr << "cpio: cannot preserve times for " << it->first << ": " << strerror(errno) << std::endl; ok=false; } } } return ok ? 0 : 1; } int runCommand(int argc,const char **argv){ char mode=0; std::string format; char inputDelimiter='\n'; bool help=false; bool createDirs=false,unconditional=false,preserveTimes=false; std::vector<std::string> operands; static const struct option longOptions[]={ {"create",no_argument,nullptr,'o'}, {"extract",no_argument,nullptr,'i'}, Loading @@ -174,7 +316,7 @@ namespace fresh { namespace cmd { opterr=0; optind=0; int option; while((option=getopt_long(argc,const_cast<char *const *>(argv),"-oitpH:vqc0?",longOptions,nullptr))!=-1){ while((option=getopt_long(argc,const_cast<char *const *>(argv),"-oitpH:vqc0dum?",longOptions,nullptr))!=-1){ char selectedMode=0; switch(option){ case 'o': case 'i': case 'p': case 't': Loading @@ -197,7 +339,17 @@ namespace fresh { namespace cmd { case '0': inputDelimiter='\0'; break; case 'd': createDirs=true; break; case 'u': unconditional=true; break; case 'm': preserveTimes=true; break; case 1: operands.emplace_back(optarg); break; case 1000: help=true; Loading Loading @@ -227,11 +379,19 @@ namespace fresh { namespace cmd { if(help){ std::cout << "Usage: cpio -o [-v] [-0] [-H FORMAT]\n" " cpio -p [-d] [-u] [-m] [--null] DIRECTORY\n" "Create a newc archive from pathnames read from standard input.\n" "Only create mode and the newc format are implemented." "Create mode uses the newc format; pass-through copies listed paths." << std::endl; return 0; } if(mode=='p'){ if(operands.size()!=1){ std::cerr << "cpio: pass-through mode requires one destination directory" << std::endl; return 1; } return copyThrough(operands[0],createDirs,unconditional,preserveTimes,inputDelimiter); } if(mode!='o'){ std::cerr << "cpio: only -o (create) mode is implemented" << std::endl; return 1; Loading Loading
commands/system/cpio.cpp +162 −2 Changes for commands/system/cpio.cpp: 162 added lines, 2 removed lines. Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include <iostream> #include <string> #include <vector> #include <cstdio> #include <cstring> #include <cerrno> Loading @@ -37,6 +38,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include <sys/stat.h> #include <sys/sysmacros.h> #include "../filesystem/copyutil.h" #include "shell.h" namespace fresh { namespace cmd { Loading Loading @@ -151,11 +153,151 @@ namespace fresh { namespace cmd { return true; } static bool normalizeRelativePath(const std::string &path,std::string &relative){ std::vector<std::string> components; size_t start=0; while(start<path.size()){ size_t slash=path.find('/',start); std::string component=path.substr(start,slash==std::string::npos ? std::string::npos : slash-start); if(component==".."){ if(components.empty()) return false; components.pop_back(); }else if(!component.empty() && component!="."){ components.push_back(component); } if(slash==std::string::npos) break; start=slash+1; } relative.clear(); for(const auto &component:components){ if(!relative.empty()) relative+='/'; relative+=component; } return true; } static int copyThrough(const std::string &destination,bool createDirs, bool unconditional,bool preserveTimes,char delimiter){ struct stat destinationStat; if(stat(destination.c_str(),&destinationStat)!=0 || !S_ISDIR(destinationStat.st_mode)){ std::cerr << "cpio: " << destination << ": not a directory" << std::endl; return 1; } copy::Options copyOptions; copyOptions.prog="cpio"; copyOptions.deref=copy::Deref::Never; copyOptions.recursive=true; copyOptions.update=!unconditional; copyOptions.keepTimes=preserveTimes; copy::Copier copier(copyOptions); std::string root=destination; while(root.size()>1 && root.back()=='/') root.pop_back(); bool ok=true; std::vector<std::pair<std::string,struct stat>> directories; std::string source; while(std::getline(std::cin,source,delimiter)){ if(source.empty()) continue; std::string relative; if(!normalizeRelativePath(source,relative)){ std::cerr << "cpio: refusing path outside destination: " << source << std::endl; ok=false; continue; } struct stat sourceStat; if(lstat(source.c_str(),&sourceStat)!=0){ std::cerr << "cpio: " << source << ": " << strerror(errno) << std::endl; ok=false; continue; } std::string target=root; size_t componentStart=0; std::vector<std::string> components; while(componentStart<relative.size()){ size_t slash=relative.find('/',componentStart); components.push_back(relative.substr(componentStart,slash==std::string::npos ? std::string::npos : slash-componentStart)); if(slash==std::string::npos) break; componentStart=slash+1; } size_t directoryCount=S_ISDIR(sourceStat.st_mode) ? components.size() : (components.empty() ? 0 : components.size()-1); bool pathReady=true; for(size_t i=0;i<directoryCount;++i){ target+="/"+components[i]; struct stat targetStat; if(lstat(target.c_str(),&targetStat)==0){ if(!S_ISDIR(targetStat.st_mode)){ std::cerr << "cpio: " << target << ": not a directory" << std::endl; pathReady=false; break; } }else if(errno==ENOENT && createDirs){ mode_t mode=(i+1==components.size() && S_ISDIR(sourceStat.st_mode)) ? sourceStat.st_mode&0777 : 0755; if(mkdir(target.c_str(),mode|S_IRWXU)!=0){ std::cerr << "cpio: cannot create directory " << target << ": " << strerror(errno) << std::endl; pathReady=false; break; } }else{ std::cerr << "cpio: cannot access directory " << target << ": " << strerror(errno) << std::endl; pathReady=false; break; } } if(!pathReady){ ok=false; continue; } if(!relative.empty()) target+="/"+components.back(); if(S_ISDIR(sourceStat.st_mode)){ if(!relative.empty()){ struct stat targetStat; if(lstat(target.c_str(),&targetStat)!=0){ if(errno!=ENOENT || !createDirs || mkdir(target.c_str(),(sourceStat.st_mode&0777)|S_IRWXU)!=0){ std::cerr << "cpio: cannot create directory " << target << ": " << strerror(errno) << std::endl; ok=false; continue; } }else if(!S_ISDIR(targetStat.st_mode)){ std::cerr << "cpio: " << target << ": not a directory" << std::endl; ok=false; continue; } } if(preserveTimes) directories.push_back({target,sourceStat}); continue; } if(!copier.copy(source,target,true)) ok=false; } if(std::cin.bad()){ std::cerr << "cpio: error reading pathname list" << std::endl; ok=false; } if(preserveTimes){ for(auto it=directories.rbegin();it!=directories.rend();++it){ struct timespec times[2]={it->second.st_atim,it->second.st_mtim}; if(utimensat(AT_FDCWD,it->first.c_str(),times,0)!=0){ std::cerr << "cpio: cannot preserve times for " << it->first << ": " << strerror(errno) << std::endl; ok=false; } } } return ok ? 0 : 1; } int runCommand(int argc,const char **argv){ char mode=0; std::string format; char inputDelimiter='\n'; bool help=false; bool createDirs=false,unconditional=false,preserveTimes=false; std::vector<std::string> operands; static const struct option longOptions[]={ {"create",no_argument,nullptr,'o'}, {"extract",no_argument,nullptr,'i'}, Loading @@ -174,7 +316,7 @@ namespace fresh { namespace cmd { opterr=0; optind=0; int option; while((option=getopt_long(argc,const_cast<char *const *>(argv),"-oitpH:vqc0?",longOptions,nullptr))!=-1){ while((option=getopt_long(argc,const_cast<char *const *>(argv),"-oitpH:vqc0dum?",longOptions,nullptr))!=-1){ char selectedMode=0; switch(option){ case 'o': case 'i': case 'p': case 't': Loading @@ -197,7 +339,17 @@ namespace fresh { namespace cmd { case '0': inputDelimiter='\0'; break; case 'd': createDirs=true; break; case 'u': unconditional=true; break; case 'm': preserveTimes=true; break; case 1: operands.emplace_back(optarg); break; case 1000: help=true; Loading Loading @@ -227,11 +379,19 @@ namespace fresh { namespace cmd { if(help){ std::cout << "Usage: cpio -o [-v] [-0] [-H FORMAT]\n" " cpio -p [-d] [-u] [-m] [--null] DIRECTORY\n" "Create a newc archive from pathnames read from standard input.\n" "Only create mode and the newc format are implemented." "Create mode uses the newc format; pass-through copies listed paths." << std::endl; return 0; } if(mode=='p'){ if(operands.size()!=1){ std::cerr << "cpio: pass-through mode requires one destination directory" << std::endl; return 1; } return copyThrough(operands[0],createDirs,unconditional,preserveTimes,inputDelimiter); } if(mode!='o'){ std::cerr << "cpio: only -o (create) mode is implemented" << std::endl; return 1; Loading