]>
git.saurik.com Git - apt.git/blob - apt-pkg/contrib/fileutl.cc
   1 // -*- mode: cpp; mode: fold -*- 
   3 // $Id: fileutl.cc,v 1.42 2002/09/14 05:29:22 jgg Exp $ 
   4 /* ###################################################################### 
   8    CopyFile - Buffered copy of a single file 
   9    GetLock - dpkg compatible lock file manipulation (fcntl) 
  11    Most of this source is placed in the Public Domain, do with it what  
  13    It was originally written by Jason Gunthorpe <jgg@debian.org>. 
  14    FileFd gzip support added by Martin Pitt <martin.pitt@canonical.com> 
  16    The exception is RunScripts() it is under the GPLv2 
  18    ##################################################################### */ 
  20 // Include Files                                                        /*{{{*/ 
  21 #include <apt-pkg/fileutl.h> 
  22 #include <apt-pkg/error.h> 
  23 #include <apt-pkg/sptr.h> 
  24 #include <apt-pkg/configuration.h> 
  35 #include <sys/types.h> 
  47 // RunScripts - Run a set of scripts from a configuration subtree       /*{{{*/ 
  48 // --------------------------------------------------------------------- 
  50 bool RunScripts(const char *Cnf
) 
  52    Configuration::Item 
const *Opts 
= _config
->Tree(Cnf
); 
  53    if (Opts 
== 0 || Opts
->Child 
== 0) 
  57    // Fork for running the system calls 
  58    pid_t Child 
= ExecFork(); 
  63       if (chdir("/tmp/") != 0) 
  66       unsigned int Count 
= 1; 
  67       for (; Opts 
!= 0; Opts 
= Opts
->Next
, Count
++) 
  69          if (Opts
->Value
.empty() == true) 
  72          if (system(Opts
->Value
.c_str()) != 0) 
  80    while (waitpid(Child
,&Status
,0) != Child
) 
  84       return _error
->Errno("waitpid","Couldn't wait for subprocess"); 
  87    // Restore sig int/quit 
  88    signal(SIGQUIT
,SIG_DFL
); 
  89    signal(SIGINT
,SIG_DFL
);    
  91    // Check for an error code. 
  92    if (WIFEXITED(Status
) == 0 || WEXITSTATUS(Status
) != 0) 
  94       unsigned int Count 
= WEXITSTATUS(Status
); 
  98          for (; Opts 
!= 0 && Count 
!= 1; Opts 
= Opts
->Next
, Count
--); 
  99          _error
->Error("Problem executing scripts %s '%s'",Cnf
,Opts
->Value
.c_str()); 
 102       return _error
->Error("Sub-process returned an error code"); 
 109 // CopyFile - Buffered copy of a file                                   /*{{{*/ 
 110 // --------------------------------------------------------------------- 
 111 /* The caller is expected to set things so that failure causes erasure */ 
 112 bool CopyFile(FileFd 
&From
,FileFd 
&To
) 
 114    if (From
.IsOpen() == false || To
.IsOpen() == false) 
 117    // Buffered copy between fds 
 118    SPtrArray
<unsigned char> Buf 
= new unsigned char[64000]; 
 119    unsigned long Size 
= From
.Size(); 
 122       unsigned long ToRead 
= Size
; 
 126       if (From
.Read(Buf
,ToRead
) == false ||  
 127           To
.Write(Buf
,ToRead
) == false) 
 136 // GetLock - Gets a lock file                                           /*{{{*/ 
 137 // --------------------------------------------------------------------- 
 138 /* This will create an empty file of the given name and lock it. Once this 
 139    is done all other calls to GetLock in any other process will fail with 
 140    -1. The return result is the fd of the file, the call should call 
 141    close at some time. */ 
 142 int GetLock(string File
,bool Errors
) 
 144    // GetLock() is used in aptitude on directories with public-write access 
 145    // Use O_NOFOLLOW here to prevent symlink traversal attacks 
 146    int FD 
= open(File
.c_str(),O_RDWR 
| O_CREAT 
| O_NOFOLLOW
,0640); 
 149       // Read only .. cant have locking problems there. 
 152          _error
->Warning(_("Not using locking for read only lock file %s"),File
.c_str()); 
 153          return dup(0);       // Need something for the caller to close 
 157          _error
->Errno("open",_("Could not open lock file %s"),File
.c_str()); 
 159       // Feh.. We do this to distinguish the lock vs open case.. 
 163    SetCloseExec(FD
,true); 
 165    // Aquire a write lock 
 168    fl
.l_whence 
= SEEK_SET
; 
 171    if (fcntl(FD
,F_SETLK
,&fl
) == -1) 
 175          _error
->Warning(_("Not using locking for nfs mounted lock file %s"),File
.c_str()); 
 176          return dup(0);       // Need something for the caller to close   
 179          _error
->Errno("open",_("Could not get lock %s"),File
.c_str()); 
 190 // FileExists - Check if a file exists                                  /*{{{*/ 
 191 // --------------------------------------------------------------------- 
 193 bool FileExists(string File
) 
 196    if (stat(File
.c_str(),&Buf
) != 0) 
 201 // GetListOfFilesInDir - returns a vector of files in the given dir     /*{{{*/ 
 202 // --------------------------------------------------------------------- 
 203 /* If an extension is given only files with this extension are included 
 204    in the returned vector, otherwise every "normal" file is included. */ 
 205 std::vector
<string
> GetListOfFilesInDir(string 
const &Dir
, string 
const &Ext
, 
 206                                         bool const &SortList
) 
 208    return GetListOfFilesInDir(Dir
, Ext
, SortList
, false); 
 210 std::vector
<string
> GetListOfFilesInDir(string 
const &Dir
, string 
const &Ext
, 
 211                                         bool const &SortList
, bool const &AllowNoExt
) 
 213    std::vector
<string
> ext
; 
 215    if (Ext
.empty() == false) 
 217    if (AllowNoExt 
== true && ext
.empty() == false) 
 219    return GetListOfFilesInDir(Dir
, ext
, SortList
); 
 221 std::vector
<string
> GetListOfFilesInDir(string 
const &Dir
, std::vector
<string
> const &Ext
, 
 222                                         bool const &SortList
) 
 224    // Attention debuggers: need to be set with the environment config file! 
 225    bool const Debug 
= _config
->FindB("Debug::GetListOfFilesInDir", false); 
 228       std::clog 
<< "Accept in " << Dir 
<< " only files with the following " << Ext
.size() << " extensions:" << std::endl
; 
 229       if (Ext
.empty() == true) 
 230          std::clog 
<< "\tNO extension" << std::endl
; 
 232          for (std::vector
<string
>::const_iterator e 
= Ext
.begin(); 
 234             std::clog 
<< '\t' << (e
->empty() == true ? "NO" : *e
) << " extension" << std::endl
; 
 237    std::vector
<string
> List
; 
 238    DIR *D 
= opendir(Dir
.c_str()); 
 241       _error
->Errno("opendir",_("Unable to read %s"),Dir
.c_str()); 
 245    for (struct dirent 
*Ent 
= readdir(D
); Ent 
!= 0; Ent 
= readdir(D
))  
 247       // skip "hidden" files 
 248       if (Ent
->d_name
[0] == '.') 
 251       // check for accepted extension: 
 252       // no extension given -> periods are bad as hell! 
 253       // extensions given -> "" extension allows no extension 
 254       if (Ext
.empty() == false) 
 256          string d_ext 
= flExtension(Ent
->d_name
); 
 257          if (d_ext 
== Ent
->d_name
) // no extension 
 259             if (std::find(Ext
.begin(), Ext
.end(), "") == Ext
.end()) 
 262                   std::clog 
<< "Bad file: " << Ent
->d_name 
<< " → no extension" << std::endl
; 
 266          else if (std::find(Ext
.begin(), Ext
.end(), d_ext
) == Ext
.end()) 
 269                std::clog 
<< "Bad file: " << Ent
->d_name 
<< " → bad extension »" << flExtension(Ent
->d_name
) << "«" << std::endl
; 
 274       // Skip bad filenames ala run-parts 
 275       const char *C 
= Ent
->d_name
; 
 277          if (isalpha(*C
) == 0 && isdigit(*C
) == 0 
 278              && *C 
!= '_' && *C 
!= '-') { 
 279             // no required extension -> dot is a bad character 
 280             if (*C 
== '.' && Ext
.empty() == false) 
 285       // we don't reach the end of the name -> bad character included 
 289             std::clog 
<< "Bad file: " << Ent
->d_name 
<< " → bad character »" 
 290                << *C 
<< "« in filename (period allowed: " << (Ext
.empty() ? "no" : "yes") << ")" << std::endl
; 
 294       // skip filenames which end with a period. These are never valid 
 298             std::clog 
<< "Bad file: " << Ent
->d_name 
<< " → Period as last character" << std::endl
; 
 302       // Make sure it is a file and not something else 
 303       string 
const File 
= flCombine(Dir
,Ent
->d_name
); 
 305       if (stat(File
.c_str(),&St
) != 0 || S_ISREG(St
.st_mode
) == 0) 
 308             std::clog 
<< "Bad file: " << Ent
->d_name 
<< " → stat says not a good file" << std::endl
; 
 313          std::clog 
<< "Accept file: " << Ent
->d_name 
<< " in " << Dir 
<< std::endl
; 
 314       List
.push_back(File
); 
 318    if (SortList 
== true) 
 319       std::sort(List
.begin(),List
.end()); 
 323 // SafeGetCWD - This is a safer getcwd that returns a dynamic string    /*{{{*/ 
 324 // --------------------------------------------------------------------- 
 325 /* We return / on failure. */ 
 328    // Stash the current dir. 
 331    if (getcwd(S
,sizeof(S
)-2) == 0) 
 333    unsigned int Len 
= strlen(S
); 
 339 // flNotDir - Strip the directory from the filename                     /*{{{*/ 
 340 // --------------------------------------------------------------------- 
 342 string 
flNotDir(string File
) 
 344    string::size_type Res 
= File
.rfind('/'); 
 345    if (Res 
== string::npos
) 
 348    return string(File
,Res
,Res 
- File
.length()); 
 351 // flNotFile - Strip the file from the directory name                   /*{{{*/ 
 352 // --------------------------------------------------------------------- 
 353 /* Result ends in a / */ 
 354 string 
flNotFile(string File
) 
 356    string::size_type Res 
= File
.rfind('/'); 
 357    if (Res 
== string::npos
) 
 360    return string(File
,0,Res
); 
 363 // flExtension - Return the extension for the file                      /*{{{*/ 
 364 // --------------------------------------------------------------------- 
 366 string 
flExtension(string File
) 
 368    string::size_type Res 
= File
.rfind('.'); 
 369    if (Res 
== string::npos
) 
 372    return string(File
,Res
,Res 
- File
.length()); 
 375 // flNoLink - If file is a symlink then deref it                        /*{{{*/ 
 376 // --------------------------------------------------------------------- 
 377 /* If the name is not a link then the returned path is the input. */ 
 378 string 
flNoLink(string File
) 
 381    if (lstat(File
.c_str(),&St
) != 0 || S_ISLNK(St
.st_mode
) == 0) 
 383    if (stat(File
.c_str(),&St
) != 0) 
 386    /* Loop resolving the link. There is no need to limit the number of  
 387       loops because the stat call above ensures that the symlink is not  
 395       if ((Res 
= readlink(NFile
.c_str(),Buffer
,sizeof(Buffer
))) <= 0 ||  
 396           (unsigned)Res 
>= sizeof(Buffer
)) 
 399       // Append or replace the previous path 
 401       if (Buffer
[0] == '/') 
 404          NFile 
= flNotFile(NFile
) + Buffer
; 
 406       // See if we are done 
 407       if (lstat(NFile
.c_str(),&St
) != 0) 
 409       if (S_ISLNK(St
.st_mode
) == 0) 
 414 // flCombine - Combine a file and a directory                           /*{{{*/ 
 415 // --------------------------------------------------------------------- 
 416 /* If the file is an absolute path then it is just returned, otherwise 
 417    the directory is pre-pended to it. */ 
 418 string 
flCombine(string Dir
,string File
) 
 420    if (File
.empty() == true) 
 423    if (File
[0] == '/' || Dir
.empty() == true) 
 425    if (File
.length() >= 2 && File
[0] == '.' && File
[1] == '/') 
 427    if (Dir
[Dir
.length()-1] == '/') 
 429    return Dir 
+ '/' + File
; 
 432 // SetCloseExec - Set the close on exec flag                            /*{{{*/ 
 433 // --------------------------------------------------------------------- 
 435 void SetCloseExec(int Fd
,bool Close
) 
 437    if (fcntl(Fd
,F_SETFD
,(Close 
== false)?0:FD_CLOEXEC
) != 0) 
 439       cerr 
<< "FATAL -> Could not set close on exec " << strerror(errno
) << endl
; 
 444 // SetNonBlock - Set the nonblocking flag                               /*{{{*/ 
 445 // --------------------------------------------------------------------- 
 447 void SetNonBlock(int Fd
,bool Block
) 
 449    int Flags 
= fcntl(Fd
,F_GETFL
) & (~O_NONBLOCK
); 
 450    if (fcntl(Fd
,F_SETFL
,Flags 
| ((Block 
== false)?0:O_NONBLOCK
)) != 0) 
 452       cerr 
<< "FATAL -> Could not set non-blocking flag " << strerror(errno
) << endl
; 
 457 // WaitFd - Wait for a FD to become readable                            /*{{{*/ 
 458 // --------------------------------------------------------------------- 
 459 /* This waits for a FD to become readable using select. It is useful for 
 460    applications making use of non-blocking sockets. The timeout is  
 462 bool WaitFd(int Fd
,bool write
,unsigned long timeout
) 
 475          Res 
= select(Fd
+1,0,&Set
,0,(timeout 
!= 0?&tv
:0)); 
 477       while (Res 
< 0 && errno 
== EINTR
); 
 487          Res 
= select(Fd
+1,&Set
,0,0,(timeout 
!= 0?&tv
:0)); 
 489       while (Res 
< 0 && errno 
== EINTR
); 
 498 // ExecFork - Magical fork that sanitizes the context before execing    /*{{{*/ 
 499 // --------------------------------------------------------------------- 
 500 /* This is used if you want to cleanse the environment for the forked  
 501    child, it fixes up the important signals and nukes all of the fds, 
 502    otherwise acts like normal fork. */ 
 505    // Fork off the process 
 506    pid_t Process 
= fork(); 
 509       cerr 
<< "FATAL -> Failed to fork." << endl
; 
 513    // Spawn the subprocess 
 517       signal(SIGPIPE
,SIG_DFL
); 
 518       signal(SIGQUIT
,SIG_DFL
); 
 519       signal(SIGINT
,SIG_DFL
); 
 520       signal(SIGWINCH
,SIG_DFL
); 
 521       signal(SIGCONT
,SIG_DFL
); 
 522       signal(SIGTSTP
,SIG_DFL
); 
 525       Configuration::Item 
const *Opts 
= _config
->Tree("APT::Keep-Fds"); 
 526       if (Opts 
!= 0 && Opts
->Child 
!= 0) 
 529          for (; Opts 
!= 0; Opts 
= Opts
->Next
) 
 531             if (Opts
->Value
.empty() == true) 
 533             int fd 
= atoi(Opts
->Value
.c_str()); 
 538       // Close all of our FDs - just in case 
 539       for (int K 
= 3; K 
!= 40; K
++) 
 541          if(KeepFDs
.find(K
) == KeepFDs
.end()) 
 542             fcntl(K
,F_SETFD
,FD_CLOEXEC
); 
 549 // ExecWait - Fancy waitpid                                             /*{{{*/ 
 550 // --------------------------------------------------------------------- 
 551 /* Waits for the given sub process. If Reap is set then no errors are  
 552    generated. Otherwise a failed subprocess will generate a proper descriptive 
 554 bool ExecWait(pid_t Pid
,const char *Name
,bool Reap
) 
 559    // Wait and collect the error code 
 561    while (waitpid(Pid
,&Status
,0) != Pid
) 
 569       return _error
->Error(_("Waited for %s but it wasn't there"),Name
); 
 573    // Check for an error code. 
 574    if (WIFEXITED(Status
) == 0 || WEXITSTATUS(Status
) != 0) 
 578       if (WIFSIGNALED(Status
) != 0) 
 580          if( WTERMSIG(Status
) == SIGSEGV
) 
 581             return _error
->Error(_("Sub-process %s received a segmentation fault."),Name
); 
 583             return _error
->Error(_("Sub-process %s received signal %u."),Name
, WTERMSIG(Status
)); 
 586       if (WIFEXITED(Status
) != 0) 
 587          return _error
->Error(_("Sub-process %s returned an error code (%u)"),Name
,WEXITSTATUS(Status
)); 
 589       return _error
->Error(_("Sub-process %s exited unexpectedly"),Name
); 
 596 // FileFd::Open - Open a file                                           /*{{{*/ 
 597 // --------------------------------------------------------------------- 
 598 /* The most commonly used open mode combinations are given with Mode */ 
 599 bool FileFd::Open(string FileName
,OpenMode Mode
, unsigned long Perms
) 
 606       iFd 
= open(FileName
.c_str(),O_RDONLY
); 
 607       if (iFd 
> 0 && FileName
.compare(FileName
.size()-3, 3, ".gz") == 0) { 
 608           gz 
= gzdopen (iFd
, "r"); 
 619          if (lstat(FileName
.c_str(),&Buf
) == 0 && S_ISLNK(Buf
.st_mode
)) 
 620             unlink(FileName
.c_str()); 
 621          iFd 
= open(FileName
.c_str(),O_RDWR 
| O_CREAT 
| O_TRUNC
,Perms
); 
 626       iFd 
= open(FileName
.c_str(),O_RDWR
); 
 630       iFd 
= open(FileName
.c_str(),O_RDWR 
| O_CREAT
,Perms
); 
 634       unlink(FileName
.c_str()); 
 635       iFd 
= open(FileName
.c_str(),O_RDWR 
| O_CREAT 
| O_EXCL
,Perms
); 
 640       return _error
->Errno("open",_("Could not open file %s"),FileName
.c_str()); 
 642    this->FileName 
= FileName
; 
 643    SetCloseExec(iFd
,true); 
 647 // FileFd::~File - Closes the file                                      /*{{{*/ 
 648 // --------------------------------------------------------------------- 
 649 /* If the proper modes are selected then we close the Fd and possibly 
 650    unlink the file on error. */ 
 656 // FileFd::Read - Read a bit of the file                                /*{{{*/ 
 657 // --------------------------------------------------------------------- 
 658 /* We are carefull to handle interruption by a signal while reading  
 660 bool FileFd::Read(void *To
,unsigned long Size
,unsigned long *Actual
) 
 670          Res 
= gzread(gz
,To
,Size
); 
 672          Res 
= read(iFd
,To
,Size
); 
 673       if (Res 
< 0 && errno 
== EINTR
) 
 678          return _error
->Errno("read",_("Read error")); 
 681       To 
= (char *)To 
+ Res
; 
 686    while (Res 
> 0 && Size 
> 0); 
 699    return _error
->Error(_("read, still have %lu to read but none left"),Size
); 
 702 // FileFd::Write - Write to the file                                    /*{{{*/ 
 703 // --------------------------------------------------------------------- 
 705 bool FileFd::Write(const void *From
,unsigned long Size
) 
 712          Res 
= gzwrite(gz
,From
,Size
); 
 714          Res 
= write(iFd
,From
,Size
); 
 715       if (Res 
< 0 && errno 
== EINTR
) 
 720          return _error
->Errno("write",_("Write error")); 
 723       From 
= (char *)From 
+ Res
; 
 726    while (Res 
> 0 && Size 
> 0); 
 732    return _error
->Error(_("write, still have %lu to write but couldn't"),Size
); 
 735 // FileFd::Seek - Seek in the file                                      /*{{{*/ 
 736 // --------------------------------------------------------------------- 
 738 bool FileFd::Seek(unsigned long To
) 
 742       res 
= gzseek(gz
,To
,SEEK_SET
); 
 744       res 
= lseek(iFd
,To
,SEEK_SET
); 
 745    if (res 
!= (signed)To
) 
 748       return _error
->Error("Unable to seek to %lu",To
); 
 754 // FileFd::Skip - Seek in the file                                      /*{{{*/ 
 755 // --------------------------------------------------------------------- 
 757 bool FileFd::Skip(unsigned long Over
) 
 761       res 
= gzseek(gz
,Over
,SEEK_CUR
); 
 763       res 
= lseek(iFd
,Over
,SEEK_CUR
); 
 767       return _error
->Error("Unable to seek ahead %lu",Over
); 
 773 // FileFd::Truncate - Truncate the file                                 /*{{{*/ 
 774 // --------------------------------------------------------------------- 
 776 bool FileFd::Truncate(unsigned long To
) 
 781       return _error
->Error("Truncating gzipped files is not implemented (%s)", FileName
.c_str()); 
 783    if (ftruncate(iFd
,To
) != 0) 
 786       return _error
->Error("Unable to truncate to %lu",To
); 
 792 // FileFd::Tell - Current seek position                                 /*{{{*/ 
 793 // --------------------------------------------------------------------- 
 795 unsigned long FileFd::Tell() 
 801      Res 
= lseek(iFd
,0,SEEK_CUR
); 
 802    if (Res 
== (off_t
)-1) 
 803       _error
->Errno("lseek","Failed to determine the current file position"); 
 807 // FileFd::Size - Return the size of the file                           /*{{{*/ 
 808 // --------------------------------------------------------------------- 
 810 unsigned long FileFd::Size() 
 812    //TODO: For gz, do we need the actual file size here or the uncompressed length? 
 814    if (fstat(iFd
,&Buf
) != 0) 
 815       return _error
->Errno("fstat","Unable to determine the file size"); 
 819 // FileFd::Close - Close the file if the close flag is set              /*{{{*/ 
 820 // --------------------------------------------------------------------- 
 825    if ((Flags 
& AutoClose
) == AutoClose
) 
 826       if ((gz 
!= NULL 
&& gzclose(gz
) != 0) || (gz 
== NULL 
&& iFd 
> 0 && close(iFd
) != 0)) 
 827          Res 
&= _error
->Errno("close",_("Problem closing the file")); 
 831    if ((Flags 
& Fail
) == Fail 
&& (Flags 
& DelOnFail
) == DelOnFail 
&& 
 832        FileName
.empty() == false) 
 833       if (unlink(FileName
.c_str()) != 0) 
 834          Res 
&= _error
->WarningE("unlnk",_("Problem unlinking the file")); 
 838 // FileFd::Sync - Sync the file                                         /*{{{*/ 
 839 // --------------------------------------------------------------------- 
 843 #ifdef _POSIX_SYNCHRONIZED_IO 
 845       return _error
->Errno("sync",_("Problem syncing the file"));