Commit 9a398b50 authored by jan.koester's avatar jan.koester
Browse files

test

parent e97d8a3b
Loading
Loading
Loading
Loading
+162 −2
Changes for commands/system/cpio.cpp: 162 added lines, 2 removed lines.
Original line number Diff line number Diff line
@@ -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>
@@ -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 {
@@ -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'},
@@ -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':
@@ -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;
@@ -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;