]> git.saurik.com Git - apt.git/blobdiff - apt-pkg/contrib/fileutl.cc
* cmdline/apt-get.cc
[apt.git] / apt-pkg / contrib / fileutl.cc
index 00afc8050f6ce64f87f1d96f8c27fbdc1f7c7609..da32983f11ab13dc2c7a7ff448cbec122a677ae5 100644 (file)
@@ -1,6 +1,6 @@
 // -*- mode: cpp; mode: fold -*-
 // Description                                                         /*{{{*/
 // -*- mode: cpp; mode: fold -*-
 // Description                                                         /*{{{*/
-// $Id: fileutl.cc,v 1.41 2002/04/24 05:02:40 jgg Exp $
+// $Id: fileutl.cc,v 1.42 2002/09/14 05:29:22 jgg Exp $
 /* ######################################################################
    
    File Utilities
 /* ######################################################################
    
    File Utilities
@@ -8,21 +8,25 @@
    CopyFile - Buffered copy of a single file
    GetLock - dpkg compatible lock file manipulation (fcntl)
    
    CopyFile - Buffered copy of a single file
    GetLock - dpkg compatible lock file manipulation (fcntl)
    
-   This source is placed in the Public Domain, do with it what you will
-   It was originally written by Jason Gunthorpe.
+   Most of this source is placed in the Public Domain, do with it what 
+   you will
+   It was originally written by Jason Gunthorpe <jgg@debian.org>.
    
    
+   The exception is RunScripts() it is under the GPLv2
+
    ##################################################################### */
                                                                        /*}}}*/
 // Include Files                                                       /*{{{*/
    ##################################################################### */
                                                                        /*}}}*/
 // Include Files                                                       /*{{{*/
-#ifdef __GNUG__
-#pragma implementation "apt-pkg/fileutl.h"
-#endif 
 #include <apt-pkg/fileutl.h>
 #include <apt-pkg/error.h>
 #include <apt-pkg/sptr.h>
 #include <apt-pkg/fileutl.h>
 #include <apt-pkg/error.h>
 #include <apt-pkg/sptr.h>
+#include <apt-pkg/configuration.h>
 
 #include <apti18n.h>
 
 
 #include <apti18n.h>
 
+#include <cstdlib>
+#include <cstring>
+
 #include <iostream>
 #include <unistd.h>
 #include <fcntl.h>
 #include <iostream>
 #include <unistd.h>
 #include <fcntl.h>
 #include <sys/types.h>
 #include <sys/time.h>
 #include <sys/wait.h>
 #include <sys/types.h>
 #include <sys/time.h>
 #include <sys/wait.h>
+#include <dirent.h>
 #include <signal.h>
 #include <errno.h>
 #include <signal.h>
 #include <errno.h>
+#include <set>
+#include <algorithm>
                                                                        /*}}}*/
 
 using namespace std;
 
                                                                        /*}}}*/
 
 using namespace std;
 
+// RunScripts - Run a set of scripts from a configuration subtree      /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+bool RunScripts(const char *Cnf)
+{
+   Configuration::Item const *Opts = _config->Tree(Cnf);
+   if (Opts == 0 || Opts->Child == 0)
+      return true;
+   Opts = Opts->Child;
+
+   // Fork for running the system calls
+   pid_t Child = ExecFork();
+   
+   // This is the child
+   if (Child == 0)
+   {
+      if (chdir("/tmp/") != 0)
+        _exit(100);
+        
+      unsigned int Count = 1;
+      for (; Opts != 0; Opts = Opts->Next, Count++)
+      {
+        if (Opts->Value.empty() == true)
+           continue;
+        
+        if (system(Opts->Value.c_str()) != 0)
+           _exit(100+Count);
+      }
+      _exit(0);
+   }      
+
+   // Wait for the child
+   int Status = 0;
+   while (waitpid(Child,&Status,0) != Child)
+   {
+      if (errno == EINTR)
+        continue;
+      return _error->Errno("waitpid","Couldn't wait for subprocess");
+   }
+
+   // Restore sig int/quit
+   signal(SIGQUIT,SIG_DFL);
+   signal(SIGINT,SIG_DFL);   
+
+   // Check for an error code.
+   if (WIFEXITED(Status) == 0 || WEXITSTATUS(Status) != 0)
+   {
+      unsigned int Count = WEXITSTATUS(Status);
+      if (Count > 100)
+      {
+        Count -= 100;
+        for (; Opts != 0 && Count != 1; Opts = Opts->Next, Count--);
+        _error->Error("Problem executing scripts %s '%s'",Cnf,Opts->Value.c_str());
+      }
+      
+      return _error->Error("Sub-process returned an error code");
+   }
+   
+   return true;
+}
+                                                                       /*}}}*/
+
 // CopyFile - Buffered copy of a file                                  /*{{{*/
 // ---------------------------------------------------------------------
 /* The caller is expected to set things so that failure causes erasure */
 // CopyFile - Buffered copy of a file                                  /*{{{*/
 // ---------------------------------------------------------------------
 /* The caller is expected to set things so that failure causes erasure */
@@ -71,7 +140,9 @@ bool CopyFile(FileFd &From,FileFd &To)
    close at some time. */
 int GetLock(string File,bool Errors)
 {
    close at some time. */
 int GetLock(string File,bool Errors)
 {
-   int FD = open(File.c_str(),O_RDWR | O_CREAT | O_TRUNC,0640);
+   // GetLock() is used in aptitude on directories with public-write access
+   // Use O_NOFOLLOW here to prevent symlink traversal attacks
+   int FD = open(File.c_str(),O_RDWR | O_CREAT | O_NOFOLLOW,0640);
    if (FD < 0)
    {
       // Read only .. cant have locking problems there.
    if (FD < 0)
    {
       // Read only .. cant have locking problems there.
@@ -126,6 +197,128 @@ bool FileExists(string File)
    return true;
 }
                                                                        /*}}}*/
    return true;
 }
                                                                        /*}}}*/
+// GetListOfFilesInDir - returns a vector of files in the given dir    /*{{{*/
+// ---------------------------------------------------------------------
+/* If an extension is given only files with this extension are included
+   in the returned vector, otherwise every "normal" file is included. */
+std::vector<string> GetListOfFilesInDir(string const &Dir, string const &Ext,
+                                       bool const &SortList)
+{
+   return GetListOfFilesInDir(Dir, Ext, SortList, false);
+}
+std::vector<string> GetListOfFilesInDir(string const &Dir, string const &Ext,
+                                       bool const &SortList, bool const &AllowNoExt)
+{
+   std::vector<string> ext;
+   ext.reserve(2);
+   if (Ext.empty() == false)
+      ext.push_back(Ext);
+   if (AllowNoExt == true && ext.empty() == false)
+      ext.push_back("");
+   return GetListOfFilesInDir(Dir, ext, SortList);
+}
+std::vector<string> GetListOfFilesInDir(string const &Dir, std::vector<string> const &Ext,
+                                       bool const &SortList)
+{
+   // Attention debuggers: need to be set with the environment config file!
+   bool const Debug = _config->FindB("Debug::GetListOfFilesInDir", false);
+   if (Debug == true)
+   {
+      std::clog << "Accept in " << Dir << " only files with the following " << Ext.size() << " extensions:" << std::endl;
+      if (Ext.empty() == true)
+        std::clog << "\tNO extension" << std::endl;
+      else
+        for (std::vector<string>::const_iterator e = Ext.begin();
+             e != Ext.end(); ++e)
+           std::clog << '\t' << (e->empty() == true ? "NO" : *e) << " extension" << std::endl;
+   }
+
+   std::vector<string> List;
+   DIR *D = opendir(Dir.c_str());
+   if (D == 0) 
+   {
+      _error->Errno("opendir",_("Unable to read %s"),Dir.c_str());
+      return List;
+   }
+
+   for (struct dirent *Ent = readdir(D); Ent != 0; Ent = readdir(D)) 
+   {
+      // skip "hidden" files
+      if (Ent->d_name[0] == '.')
+        continue;
+
+      // check for accepted extension:
+      // no extension given -> periods are bad as hell!
+      // extensions given -> "" extension allows no extension
+      if (Ext.empty() == false)
+      {
+        string d_ext = flExtension(Ent->d_name);
+        if (d_ext == Ent->d_name) // no extension
+        {
+           if (std::find(Ext.begin(), Ext.end(), "") == Ext.end())
+           {
+              if (Debug == true)
+                 std::clog << "Bad file: " << Ent->d_name << " → no extension" << std::endl;
+              continue;
+           }
+        }
+        else if (std::find(Ext.begin(), Ext.end(), d_ext) == Ext.end())
+        {
+           if (Debug == true)
+              std::clog << "Bad file: " << Ent->d_name << " → bad extension »" << flExtension(Ent->d_name) << "«" << std::endl;
+           continue;
+        }
+      }
+
+      // Skip bad filenames ala run-parts
+      const char *C = Ent->d_name;
+      for (; *C != 0; ++C)
+        if (isalpha(*C) == 0 && isdigit(*C) == 0
+            && *C != '_' && *C != '-') {
+           // no required extension -> dot is a bad character
+           if (*C == '.' && Ext.empty() == false)
+              continue;
+           break;
+        }
+
+      // we don't reach the end of the name -> bad character included
+      if (*C != 0)
+      {
+        if (Debug == true)
+           std::clog << "Bad file: " << Ent->d_name << " → bad character »"
+              << *C << "« in filename (period allowed: " << (Ext.empty() ? "no" : "yes") << ")" << std::endl;
+        continue;
+      }
+
+      // skip filenames which end with a period. These are never valid
+      if (*(C - 1) == '.')
+      {
+        if (Debug == true)
+           std::clog << "Bad file: " << Ent->d_name << " → Period as last character" << std::endl;
+        continue;
+      }
+
+      // Make sure it is a file and not something else
+      string const File = flCombine(Dir,Ent->d_name);
+      struct stat St;
+      if (stat(File.c_str(),&St) != 0 || S_ISREG(St.st_mode) == 0)
+      {
+        if (Debug == true)
+           std::clog << "Bad file: " << Ent->d_name << " → stat says not a good file" << std::endl;
+        continue;
+      }
+
+      if (Debug == true)
+        std::clog << "Accept file: " << Ent->d_name << " in " << Dir << std::endl;
+      List.push_back(File);
+   }
+   closedir(D);
+
+   if (SortList == true)
+      std::sort(List.begin(),List.end());
+   return List;
+}
+                                                                       /*}}}*/
 // SafeGetCWD - This is a safer getcwd that returns a dynamic string   /*{{{*/
 // ---------------------------------------------------------------------
 /* We return / on failure. */
 // SafeGetCWD - This is a safer getcwd that returns a dynamic string   /*{{{*/
 // ---------------------------------------------------------------------
 /* We return / on failure. */
@@ -306,7 +499,7 @@ bool WaitFd(int Fd,bool write,unsigned long timeout)
 /* This is used if you want to cleanse the environment for the forked 
    child, it fixes up the important signals and nukes all of the fds,
    otherwise acts like normal fork. */
 /* This is used if you want to cleanse the environment for the forked 
    child, it fixes up the important signals and nukes all of the fds,
    otherwise acts like normal fork. */
-int ExecFork()
+pid_t ExecFork()
 {
    // Fork off the process
    pid_t Process = fork();
 {
    // Fork off the process
    pid_t Process = fork();
@@ -326,10 +519,27 @@ int ExecFork()
       signal(SIGWINCH,SIG_DFL);
       signal(SIGCONT,SIG_DFL);
       signal(SIGTSTP,SIG_DFL);
       signal(SIGWINCH,SIG_DFL);
       signal(SIGCONT,SIG_DFL);
       signal(SIGTSTP,SIG_DFL);
-      
+
+      set<int> KeepFDs;
+      Configuration::Item const *Opts = _config->Tree("APT::Keep-Fds");
+      if (Opts != 0 && Opts->Child != 0)
+      {
+        Opts = Opts->Child;
+        for (; Opts != 0; Opts = Opts->Next)
+        {
+           if (Opts->Value.empty() == true)
+              continue;
+           int fd = atoi(Opts->Value.c_str());
+           KeepFDs.insert(fd);
+        }
+      }
+
       // Close all of our FDs - just in case
       for (int K = 3; K != 40; K++)
       // Close all of our FDs - just in case
       for (int K = 3; K != 40; K++)
-        fcntl(K,F_SETFD,FD_CLOEXEC);
+      {
+        if(KeepFDs.find(K) == KeepFDs.end())
+           fcntl(K,F_SETFD,FD_CLOEXEC);
+      }
    }
    
    return Process;
    }
    
    return Process;
@@ -340,7 +550,7 @@ int ExecFork()
 /* Waits for the given sub process. If Reap is set then no errors are 
    generated. Otherwise a failed subprocess will generate a proper descriptive
    message */
 /* Waits for the given sub process. If Reap is set then no errors are 
    generated. Otherwise a failed subprocess will generate a proper descriptive
    message */
-bool ExecWait(int Pid,const char *Name,bool Reap)
+bool ExecWait(pid_t Pid,const char *Name,bool Reap)
 {
    if (Pid <= 1)
       return true;
 {
    if (Pid <= 1)
       return true;
@@ -355,7 +565,7 @@ bool ExecWait(int Pid,const char *Name,bool Reap)
       if (Reap == true)
         return false;
       
       if (Reap == true)
         return false;
       
-      return _error->Error(_("Waited, for %s but it wasn't there"),Name);
+      return _error->Error(_("Waited for %s but it wasn't there"),Name);
    }
 
    
    }
 
    
@@ -364,8 +574,13 @@ bool ExecWait(int Pid,const char *Name,bool Reap)
    {
       if (Reap == true)
         return false;
    {
       if (Reap == true)
         return false;
-      if (WIFSIGNALED(Status) != 0 && WTERMSIG(Status) == SIGSEGV)
-        return _error->Error(_("Sub-process %s received a segmentation fault."),Name);
+      if (WIFSIGNALED(Status) != 0)
+      {
+        if( WTERMSIG(Status) == SIGSEGV)
+           return _error->Error(_("Sub-process %s received a segmentation fault."),Name);
+        else 
+           return _error->Error(_("Sub-process %s received signal %u."),Name, WTERMSIG(Status));
+      }
 
       if (WIFEXITED(Status) != 0)
         return _error->Error(_("Sub-process %s returned an error code (%u)"),Name,WEXITSTATUS(Status));
 
       if (WIFEXITED(Status) != 0)
         return _error->Error(_("Sub-process %s returned an error code (%u)"),Name,WEXITSTATUS(Status));