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 /*{{{*/
23 #include <apt-pkg/fileutl.h>
24 #include <apt-pkg/strutl.h>
25 #include <apt-pkg/error.h>
26 #include <apt-pkg/sptr.h>
27 #include <apt-pkg/aptconfiguration.h>
28 #include <apt-pkg/configuration.h>
38 #include <sys/types.h>
56 #ifdef WORDS_BIGENDIAN
80 APT::Configuration::Compressor compressor
;
81 unsigned int openmode
;
82 unsigned long long seekpos
;
83 FileFdPrivate() : gz(NULL
), bz2(NULL
),
84 compressed_fd(-1), compressor_pid(-1), pipe(false),
85 openmode(0), seekpos(0) {};
86 bool CloseDown(std::string
const &FileName
)
91 int const e
= gzclose(gz
);
93 // gzdclose() on empty files always fails with "buffer error" here, ignore that
94 if (e
!= 0 && e
!= Z_BUF_ERROR
)
95 Res
&= _error
->Errno("close",_("Problem closing the gzip file %s"), FileName
.c_str());
104 if (compressor_pid
> 0)
105 ExecWait(compressor_pid
, "FileFdCompressor", true);
110 ~FileFdPrivate() { CloseDown(""); }
113 // RunScripts - Run a set of scripts from a configuration subtree /*{{{*/
114 // ---------------------------------------------------------------------
116 bool RunScripts(const char *Cnf
)
118 Configuration::Item
const *Opts
= _config
->Tree(Cnf
);
119 if (Opts
== 0 || Opts
->Child
== 0)
123 // Fork for running the system calls
124 pid_t Child
= ExecFork();
129 if (_config
->FindDir("DPkg::Chroot-Directory","/") != "/")
131 std::cerr
<< "Chrooting into "
132 << _config
->FindDir("DPkg::Chroot-Directory")
134 if (chroot(_config
->FindDir("DPkg::Chroot-Directory","/").c_str()) != 0)
138 if (chdir("/tmp/") != 0)
141 unsigned int Count
= 1;
142 for (; Opts
!= 0; Opts
= Opts
->Next
, Count
++)
144 if (Opts
->Value
.empty() == true)
147 if (system(Opts
->Value
.c_str()) != 0)
153 // Wait for the child
155 while (waitpid(Child
,&Status
,0) != Child
)
159 return _error
->Errno("waitpid","Couldn't wait for subprocess");
162 // Restore sig int/quit
163 signal(SIGQUIT
,SIG_DFL
);
164 signal(SIGINT
,SIG_DFL
);
166 // Check for an error code.
167 if (WIFEXITED(Status
) == 0 || WEXITSTATUS(Status
) != 0)
169 unsigned int Count
= WEXITSTATUS(Status
);
173 for (; Opts
!= 0 && Count
!= 1; Opts
= Opts
->Next
, Count
--);
174 _error
->Error("Problem executing scripts %s '%s'",Cnf
,Opts
->Value
.c_str());
177 return _error
->Error("Sub-process returned an error code");
184 // CopyFile - Buffered copy of a file /*{{{*/
185 // ---------------------------------------------------------------------
186 /* The caller is expected to set things so that failure causes erasure */
187 bool CopyFile(FileFd
&From
,FileFd
&To
)
189 if (From
.IsOpen() == false || To
.IsOpen() == false ||
190 From
.Failed() == true || To
.Failed() == true)
193 // Buffered copy between fds
194 SPtrArray
<unsigned char> Buf
= new unsigned char[64000];
195 unsigned long long Size
= From
.Size();
198 unsigned long long ToRead
= Size
;
202 if (From
.Read(Buf
,ToRead
) == false ||
203 To
.Write(Buf
,ToRead
) == false)
212 // GetLock - Gets a lock file /*{{{*/
213 // ---------------------------------------------------------------------
214 /* This will create an empty file of the given name and lock it. Once this
215 is done all other calls to GetLock in any other process will fail with
216 -1. The return result is the fd of the file, the call should call
217 close at some time. */
218 int GetLock(string File
,bool Errors
)
220 // GetLock() is used in aptitude on directories with public-write access
221 // Use O_NOFOLLOW here to prevent symlink traversal attacks
222 int FD
= open(File
.c_str(),O_RDWR
| O_CREAT
| O_NOFOLLOW
,0640);
225 // Read only .. cant have locking problems there.
228 _error
->Warning(_("Not using locking for read only lock file %s"),File
.c_str());
229 return dup(0); // Need something for the caller to close
233 _error
->Errno("open",_("Could not open lock file %s"),File
.c_str());
235 // Feh.. We do this to distinguish the lock vs open case..
239 SetCloseExec(FD
,true);
241 // Aquire a write lock
244 fl
.l_whence
= SEEK_SET
;
247 if (fcntl(FD
,F_SETLK
,&fl
) == -1)
249 // always close to not leak resources
256 _error
->Warning(_("Not using locking for nfs mounted lock file %s"),File
.c_str());
257 return dup(0); // Need something for the caller to close
261 _error
->Errno("open",_("Could not get lock %s"),File
.c_str());
269 // FileExists - Check if a file exists /*{{{*/
270 // ---------------------------------------------------------------------
271 /* Beware: Directories are also files! */
272 bool FileExists(string File
)
275 if (stat(File
.c_str(),&Buf
) != 0)
280 // RealFileExists - Check if a file exists and if it is really a file /*{{{*/
281 // ---------------------------------------------------------------------
283 bool RealFileExists(string File
)
286 if (stat(File
.c_str(),&Buf
) != 0)
288 return ((Buf
.st_mode
& S_IFREG
) != 0);
291 // DirectoryExists - Check if a directory exists and is really one /*{{{*/
292 // ---------------------------------------------------------------------
294 bool DirectoryExists(string
const &Path
)
297 if (stat(Path
.c_str(),&Buf
) != 0)
299 return ((Buf
.st_mode
& S_IFDIR
) != 0);
302 // CreateDirectory - poor man's mkdir -p guarded by a parent directory /*{{{*/
303 // ---------------------------------------------------------------------
304 /* This method will create all directories needed for path in good old
305 mkdir -p style but refuses to do this if Parent is not a prefix of
306 this Path. Example: /var/cache/ and /var/cache/apt/archives are given,
307 so it will create apt/archives if /var/cache exists - on the other
308 hand if the parent is /var/lib the creation will fail as this path
309 is not a parent of the path to be generated. */
310 bool CreateDirectory(string
const &Parent
, string
const &Path
)
312 if (Parent
.empty() == true || Path
.empty() == true)
315 if (DirectoryExists(Path
) == true)
318 if (DirectoryExists(Parent
) == false)
321 // we are not going to create directories "into the blue"
322 if (Path
.find(Parent
, 0) != 0)
325 vector
<string
> const dirs
= VectorizeString(Path
.substr(Parent
.size()), '/');
326 string progress
= Parent
;
327 for (vector
<string
>::const_iterator d
= dirs
.begin(); d
!= dirs
.end(); ++d
)
329 if (d
->empty() == true)
332 progress
.append("/").append(*d
);
333 if (DirectoryExists(progress
) == true)
336 if (mkdir(progress
.c_str(), 0755) != 0)
342 // CreateAPTDirectoryIfNeeded - ensure that the given directory exists /*{{{*/
343 // ---------------------------------------------------------------------
344 /* a small wrapper around CreateDirectory to check if it exists and to
345 remove the trailing "/apt/" from the parent directory if needed */
346 bool CreateAPTDirectoryIfNeeded(string
const &Parent
, string
const &Path
)
348 if (DirectoryExists(Path
) == true)
351 size_t const len
= Parent
.size();
352 if (len
> 5 && Parent
.find("/apt/", len
- 6, 5) == len
- 5)
354 if (CreateDirectory(Parent
.substr(0,len
-5), Path
) == true)
357 else if (CreateDirectory(Parent
, Path
) == true)
363 // GetListOfFilesInDir - returns a vector of files in the given dir /*{{{*/
364 // ---------------------------------------------------------------------
365 /* If an extension is given only files with this extension are included
366 in the returned vector, otherwise every "normal" file is included. */
367 std::vector
<string
> GetListOfFilesInDir(string
const &Dir
, string
const &Ext
,
368 bool const &SortList
, bool const &AllowNoExt
)
370 std::vector
<string
> ext
;
372 if (Ext
.empty() == false)
374 if (AllowNoExt
== true && ext
.empty() == false)
376 return GetListOfFilesInDir(Dir
, ext
, SortList
);
378 std::vector
<string
> GetListOfFilesInDir(string
const &Dir
, std::vector
<string
> const &Ext
,
379 bool const &SortList
)
381 // Attention debuggers: need to be set with the environment config file!
382 bool const Debug
= _config
->FindB("Debug::GetListOfFilesInDir", false);
385 std::clog
<< "Accept in " << Dir
<< " only files with the following " << Ext
.size() << " extensions:" << std::endl
;
386 if (Ext
.empty() == true)
387 std::clog
<< "\tNO extension" << std::endl
;
389 for (std::vector
<string
>::const_iterator e
= Ext
.begin();
391 std::clog
<< '\t' << (e
->empty() == true ? "NO" : *e
) << " extension" << std::endl
;
394 std::vector
<string
> List
;
396 if (DirectoryExists(Dir
) == false)
398 _error
->Error(_("List of files can't be created as '%s' is not a directory"), Dir
.c_str());
402 Configuration::MatchAgainstConfig
SilentIgnore("Dir::Ignore-Files-Silently");
403 DIR *D
= opendir(Dir
.c_str());
406 _error
->Errno("opendir",_("Unable to read %s"),Dir
.c_str());
410 for (struct dirent
*Ent
= readdir(D
); Ent
!= 0; Ent
= readdir(D
))
412 // skip "hidden" files
413 if (Ent
->d_name
[0] == '.')
416 // Make sure it is a file and not something else
417 string
const File
= flCombine(Dir
,Ent
->d_name
);
418 #ifdef _DIRENT_HAVE_D_TYPE
419 if (Ent
->d_type
!= DT_REG
)
422 if (RealFileExists(File
) == false)
424 // do not show ignoration warnings for directories
426 #ifdef _DIRENT_HAVE_D_TYPE
427 Ent
->d_type
== DT_DIR
||
429 DirectoryExists(File
) == true)
431 if (SilentIgnore
.Match(Ent
->d_name
) == false)
432 _error
->Notice(_("Ignoring '%s' in directory '%s' as it is not a regular file"), Ent
->d_name
, Dir
.c_str());
437 // check for accepted extension:
438 // no extension given -> periods are bad as hell!
439 // extensions given -> "" extension allows no extension
440 if (Ext
.empty() == false)
442 string d_ext
= flExtension(Ent
->d_name
);
443 if (d_ext
== Ent
->d_name
) // no extension
445 if (std::find(Ext
.begin(), Ext
.end(), "") == Ext
.end())
448 std::clog
<< "Bad file: " << Ent
->d_name
<< " → no extension" << std::endl
;
449 if (SilentIgnore
.Match(Ent
->d_name
) == false)
450 _error
->Notice(_("Ignoring file '%s' in directory '%s' as it has no filename extension"), Ent
->d_name
, Dir
.c_str());
454 else if (std::find(Ext
.begin(), Ext
.end(), d_ext
) == Ext
.end())
457 std::clog
<< "Bad file: " << Ent
->d_name
<< " → bad extension »" << flExtension(Ent
->d_name
) << "«" << std::endl
;
458 if (SilentIgnore
.Match(Ent
->d_name
) == false)
459 _error
->Notice(_("Ignoring file '%s' in directory '%s' as it has an invalid filename extension"), Ent
->d_name
, Dir
.c_str());
464 // Skip bad filenames ala run-parts
465 const char *C
= Ent
->d_name
;
467 if (isalpha(*C
) == 0 && isdigit(*C
) == 0
468 && *C
!= '_' && *C
!= '-') {
469 // no required extension -> dot is a bad character
470 if (*C
== '.' && Ext
.empty() == false)
475 // we don't reach the end of the name -> bad character included
479 std::clog
<< "Bad file: " << Ent
->d_name
<< " → bad character »"
480 << *C
<< "« in filename (period allowed: " << (Ext
.empty() ? "no" : "yes") << ")" << std::endl
;
484 // skip filenames which end with a period. These are never valid
488 std::clog
<< "Bad file: " << Ent
->d_name
<< " → Period as last character" << std::endl
;
493 std::clog
<< "Accept file: " << Ent
->d_name
<< " in " << Dir
<< std::endl
;
494 List
.push_back(File
);
498 if (SortList
== true)
499 std::sort(List
.begin(),List
.end());
502 std::vector
<string
> GetListOfFilesInDir(string
const &Dir
, bool SortList
)
504 bool const Debug
= _config
->FindB("Debug::GetListOfFilesInDir", false);
506 std::clog
<< "Accept in " << Dir
<< " all regular files" << std::endl
;
508 std::vector
<string
> List
;
510 if (DirectoryExists(Dir
) == false)
512 _error
->Error(_("List of files can't be created as '%s' is not a directory"), Dir
.c_str());
516 DIR *D
= opendir(Dir
.c_str());
519 _error
->Errno("opendir",_("Unable to read %s"),Dir
.c_str());
523 for (struct dirent
*Ent
= readdir(D
); Ent
!= 0; Ent
= readdir(D
))
525 // skip "hidden" files
526 if (Ent
->d_name
[0] == '.')
529 // Make sure it is a file and not something else
530 string
const File
= flCombine(Dir
,Ent
->d_name
);
531 #ifdef _DIRENT_HAVE_D_TYPE
532 if (Ent
->d_type
!= DT_REG
)
535 if (RealFileExists(File
) == false)
538 std::clog
<< "Bad file: " << Ent
->d_name
<< " → it is not a real file" << std::endl
;
543 // Skip bad filenames ala run-parts
544 const char *C
= Ent
->d_name
;
546 if (isalpha(*C
) == 0 && isdigit(*C
) == 0
547 && *C
!= '_' && *C
!= '-' && *C
!= '.')
550 // we don't reach the end of the name -> bad character included
554 std::clog
<< "Bad file: " << Ent
->d_name
<< " → bad character »" << *C
<< "« in filename" << std::endl
;
558 // skip filenames which end with a period. These are never valid
562 std::clog
<< "Bad file: " << Ent
->d_name
<< " → Period as last character" << std::endl
;
567 std::clog
<< "Accept file: " << Ent
->d_name
<< " in " << Dir
<< std::endl
;
568 List
.push_back(File
);
572 if (SortList
== true)
573 std::sort(List
.begin(),List
.end());
577 // SafeGetCWD - This is a safer getcwd that returns a dynamic string /*{{{*/
578 // ---------------------------------------------------------------------
579 /* We return / on failure. */
582 // Stash the current dir.
585 if (getcwd(S
,sizeof(S
)-2) == 0)
587 unsigned int Len
= strlen(S
);
593 // GetModificationTime - Get the mtime of the given file or -1 on error /*{{{*/
594 // ---------------------------------------------------------------------
595 /* We return / on failure. */
596 time_t GetModificationTime(string
const &Path
)
599 if (stat(Path
.c_str(), &St
) < 0)
604 // flNotDir - Strip the directory from the filename /*{{{*/
605 // ---------------------------------------------------------------------
607 string
flNotDir(string File
)
609 string::size_type Res
= File
.rfind('/');
610 if (Res
== string::npos
)
613 return string(File
,Res
,Res
- File
.length());
616 // flNotFile - Strip the file from the directory name /*{{{*/
617 // ---------------------------------------------------------------------
618 /* Result ends in a / */
619 string
flNotFile(string File
)
621 string::size_type Res
= File
.rfind('/');
622 if (Res
== string::npos
)
625 return string(File
,0,Res
);
628 // flExtension - Return the extension for the file /*{{{*/
629 // ---------------------------------------------------------------------
631 string
flExtension(string File
)
633 string::size_type Res
= File
.rfind('.');
634 if (Res
== string::npos
)
637 return string(File
,Res
,Res
- File
.length());
640 // flNoLink - If file is a symlink then deref it /*{{{*/
641 // ---------------------------------------------------------------------
642 /* If the name is not a link then the returned path is the input. */
643 string
flNoLink(string File
)
646 if (lstat(File
.c_str(),&St
) != 0 || S_ISLNK(St
.st_mode
) == 0)
648 if (stat(File
.c_str(),&St
) != 0)
651 /* Loop resolving the link. There is no need to limit the number of
652 loops because the stat call above ensures that the symlink is not
660 if ((Res
= readlink(NFile
.c_str(),Buffer
,sizeof(Buffer
))) <= 0 ||
661 (size_t)Res
>= sizeof(Buffer
))
664 // Append or replace the previous path
666 if (Buffer
[0] == '/')
669 NFile
= flNotFile(NFile
) + Buffer
;
671 // See if we are done
672 if (lstat(NFile
.c_str(),&St
) != 0)
674 if (S_ISLNK(St
.st_mode
) == 0)
679 // flCombine - Combine a file and a directory /*{{{*/
680 // ---------------------------------------------------------------------
681 /* If the file is an absolute path then it is just returned, otherwise
682 the directory is pre-pended to it. */
683 string
flCombine(string Dir
,string File
)
685 if (File
.empty() == true)
688 if (File
[0] == '/' || Dir
.empty() == true)
690 if (File
.length() >= 2 && File
[0] == '.' && File
[1] == '/')
692 if (Dir
[Dir
.length()-1] == '/')
694 return Dir
+ '/' + File
;
697 // SetCloseExec - Set the close on exec flag /*{{{*/
698 // ---------------------------------------------------------------------
700 void SetCloseExec(int Fd
,bool Close
)
702 if (fcntl(Fd
,F_SETFD
,(Close
== false)?0:FD_CLOEXEC
) != 0)
704 cerr
<< "FATAL -> Could not set close on exec " << strerror(errno
) << endl
;
709 // SetNonBlock - Set the nonblocking flag /*{{{*/
710 // ---------------------------------------------------------------------
712 void SetNonBlock(int Fd
,bool Block
)
714 int Flags
= fcntl(Fd
,F_GETFL
) & (~O_NONBLOCK
);
715 if (fcntl(Fd
,F_SETFL
,Flags
| ((Block
== false)?0:O_NONBLOCK
)) != 0)
717 cerr
<< "FATAL -> Could not set non-blocking flag " << strerror(errno
) << endl
;
722 // WaitFd - Wait for a FD to become readable /*{{{*/
723 // ---------------------------------------------------------------------
724 /* This waits for a FD to become readable using select. It is useful for
725 applications making use of non-blocking sockets. The timeout is
727 bool WaitFd(int Fd
,bool write
,unsigned long timeout
)
740 Res
= select(Fd
+1,0,&Set
,0,(timeout
!= 0?&tv
:0));
742 while (Res
< 0 && errno
== EINTR
);
752 Res
= select(Fd
+1,&Set
,0,0,(timeout
!= 0?&tv
:0));
754 while (Res
< 0 && errno
== EINTR
);
763 // MergeKeepFdsFromConfiguration - Merge APT::Keep-Fds configuration /*{{{*/
764 // ---------------------------------------------------------------------
765 /* This is used to merge the APT::Keep-Fds with the provided KeepFDs
768 void MergeKeepFdsFromConfiguration(std::set
<int> &KeepFDs
)
770 Configuration::Item
const *Opts
= _config
->Tree("APT::Keep-Fds");
771 if (Opts
!= 0 && Opts
->Child
!= 0)
774 for (; Opts
!= 0; Opts
= Opts
->Next
)
776 if (Opts
->Value
.empty() == true)
778 int fd
= atoi(Opts
->Value
.c_str());
784 // ExecFork - Magical fork that sanitizes the context before execing /*{{{*/
785 // ---------------------------------------------------------------------
786 /* This is used if you want to cleanse the environment for the forked
787 child, it fixes up the important signals and nukes all of the fds,
788 otherwise acts like normal fork. */
792 // we need to merge the Keep-Fds as external tools like
793 // debconf-apt-progress use it
794 MergeKeepFdsFromConfiguration(KeepFDs
);
795 return ExecFork(KeepFDs
);
798 pid_t
ExecFork(std::set
<int> KeepFDs
)
800 // Fork off the process
801 pid_t Process
= fork();
804 cerr
<< "FATAL -> Failed to fork." << endl
;
808 // Spawn the subprocess
812 signal(SIGPIPE
,SIG_DFL
);
813 signal(SIGQUIT
,SIG_DFL
);
814 signal(SIGINT
,SIG_DFL
);
815 signal(SIGWINCH
,SIG_DFL
);
816 signal(SIGCONT
,SIG_DFL
);
817 signal(SIGTSTP
,SIG_DFL
);
819 // Close all of our FDs - just in case
820 for (int K
= 3; K
!= sysconf(_SC_OPEN_MAX
); K
++)
822 if(KeepFDs
.find(K
) == KeepFDs
.end())
823 fcntl(K
,F_SETFD
,FD_CLOEXEC
);
830 // ExecWait - Fancy waitpid /*{{{*/
831 // ---------------------------------------------------------------------
832 /* Waits for the given sub process. If Reap is set then no errors are
833 generated. Otherwise a failed subprocess will generate a proper descriptive
835 bool ExecWait(pid_t Pid
,const char *Name
,bool Reap
)
840 // Wait and collect the error code
842 while (waitpid(Pid
,&Status
,0) != Pid
)
850 return _error
->Error(_("Waited for %s but it wasn't there"),Name
);
854 // Check for an error code.
855 if (WIFEXITED(Status
) == 0 || WEXITSTATUS(Status
) != 0)
859 if (WIFSIGNALED(Status
) != 0)
861 if( WTERMSIG(Status
) == SIGSEGV
)
862 return _error
->Error(_("Sub-process %s received a segmentation fault."),Name
);
864 return _error
->Error(_("Sub-process %s received signal %u."),Name
, WTERMSIG(Status
));
867 if (WIFEXITED(Status
) != 0)
868 return _error
->Error(_("Sub-process %s returned an error code (%u)"),Name
,WEXITSTATUS(Status
));
870 return _error
->Error(_("Sub-process %s exited unexpectedly"),Name
);
877 // FileFd::Open - Open a file /*{{{*/
878 // ---------------------------------------------------------------------
879 /* The most commonly used open mode combinations are given with Mode */
880 bool FileFd::Open(string FileName
,unsigned int const Mode
,CompressMode Compress
, unsigned long const Perms
)
882 if (Mode
== ReadOnlyGzip
)
883 return Open(FileName
, ReadOnly
, Gzip
, Perms
);
885 if (Compress
== Auto
&& (Mode
& WriteOnly
) == WriteOnly
)
886 return FileFdError("Autodetection on %s only works in ReadOnly openmode!", FileName
.c_str());
888 std::vector
<APT::Configuration::Compressor
> const compressors
= APT::Configuration::getCompressors();
889 std::vector
<APT::Configuration::Compressor
>::const_iterator compressor
= compressors
.begin();
890 if (Compress
== Auto
)
892 for (; compressor
!= compressors
.end(); ++compressor
)
894 std::string file
= std::string(FileName
).append(compressor
->Extension
);
895 if (FileExists(file
) == false)
901 else if (Compress
== Extension
)
903 std::string::size_type
const found
= FileName
.find_last_of('.');
905 if (found
!= std::string::npos
)
907 ext
= FileName
.substr(found
);
908 if (ext
== ".new" || ext
== ".bak")
910 std::string::size_type
const found2
= FileName
.find_last_of('.', found
- 1);
911 if (found2
!= std::string::npos
)
912 ext
= FileName
.substr(found2
, found
- found2
);
917 for (; compressor
!= compressors
.end(); ++compressor
)
918 if (ext
== compressor
->Extension
)
920 // no matching extension - assume uncompressed (imagine files like 'example.org_Packages')
921 if (compressor
== compressors
.end())
922 for (compressor
= compressors
.begin(); compressor
!= compressors
.end(); ++compressor
)
923 if (compressor
->Name
== ".")
931 case None
: name
= "."; break;
932 case Gzip
: name
= "gzip"; break;
933 case Bzip2
: name
= "bzip2"; break;
934 case Lzma
: name
= "lzma"; break;
935 case Xz
: name
= "xz"; break;
939 return FileFdError("Opening File %s in None, Auto or Extension should be already handled?!?", FileName
.c_str());
941 for (; compressor
!= compressors
.end(); ++compressor
)
942 if (compressor
->Name
== name
)
944 if (compressor
== compressors
.end())
945 return FileFdError("Can't find a configured compressor %s for file %s", name
.c_str(), FileName
.c_str());
948 if (compressor
== compressors
.end())
949 return FileFdError("Can't find a match for specified compressor mode for file %s", FileName
.c_str());
950 return Open(FileName
, Mode
, *compressor
, Perms
);
952 bool FileFd::Open(string FileName
,unsigned int const Mode
,APT::Configuration::Compressor
const &compressor
, unsigned long const Perms
)
957 if ((Mode
& WriteOnly
) != WriteOnly
&& (Mode
& (Atomic
| Create
| Empty
| Exclusive
)) != 0)
958 return FileFdError("ReadOnly mode for %s doesn't accept additional flags!", FileName
.c_str());
959 if ((Mode
& ReadWrite
) == 0)
960 return FileFdError("No openmode provided in FileFd::Open for %s", FileName
.c_str());
962 if ((Mode
& Atomic
) == Atomic
)
966 else if ((Mode
& (Exclusive
| Create
)) == (Exclusive
| Create
))
968 // for atomic, this will be done by rename in Close()
969 unlink(FileName
.c_str());
971 if ((Mode
& Empty
) == Empty
)
974 if (lstat(FileName
.c_str(),&Buf
) == 0 && S_ISLNK(Buf
.st_mode
))
975 unlink(FileName
.c_str());
979 #define if_FLAGGED_SET(FLAG, MODE) if ((Mode & FLAG) == FLAG) fileflags |= MODE
980 if_FLAGGED_SET(ReadWrite
, O_RDWR
);
981 else if_FLAGGED_SET(ReadOnly
, O_RDONLY
);
982 else if_FLAGGED_SET(WriteOnly
, O_WRONLY
);
984 if_FLAGGED_SET(Create
, O_CREAT
);
985 if_FLAGGED_SET(Empty
, O_TRUNC
);
986 if_FLAGGED_SET(Exclusive
, O_EXCL
);
987 #undef if_FLAGGED_SET
989 if ((Mode
& Atomic
) == Atomic
)
991 char *name
= strdup((FileName
+ ".XXXXXX").c_str());
993 if((iFd
= mkstemp(name
)) == -1)
996 return FileFdErrno("mkstemp", "Could not create temporary file for %s", FileName
.c_str());
999 TemporaryFileName
= string(name
);
1002 if(Perms
!= 600 && fchmod(iFd
, Perms
) == -1)
1003 return FileFdErrno("fchmod", "Could not change permissions for temporary file %s", TemporaryFileName
.c_str());
1006 iFd
= open(FileName
.c_str(), fileflags
, Perms
);
1008 this->FileName
= FileName
;
1009 if (iFd
== -1 || OpenInternDescriptor(Mode
, compressor
) == false)
1016 return FileFdErrno("open",_("Could not open file %s"), FileName
.c_str());
1019 SetCloseExec(iFd
,true);
1023 // FileFd::OpenDescriptor - Open a filedescriptor /*{{{*/
1024 // ---------------------------------------------------------------------
1026 bool FileFd::OpenDescriptor(int Fd
, unsigned int const Mode
, CompressMode Compress
, bool AutoClose
)
1028 std::vector
<APT::Configuration::Compressor
> const compressors
= APT::Configuration::getCompressors();
1029 std::vector
<APT::Configuration::Compressor
>::const_iterator compressor
= compressors
.begin();
1032 // compat with the old API
1033 if (Mode
== ReadOnlyGzip
&& Compress
== None
)
1038 case None
: name
= "."; break;
1039 case Gzip
: name
= "gzip"; break;
1040 case Bzip2
: name
= "bzip2"; break;
1041 case Lzma
: name
= "lzma"; break;
1042 case Xz
: name
= "xz"; break;
1045 if (AutoClose
== true && Fd
!= -1)
1047 return FileFdError("Opening Fd %d in Auto or Extension compression mode is not supported", Fd
);
1049 for (; compressor
!= compressors
.end(); ++compressor
)
1050 if (compressor
->Name
== name
)
1052 if (compressor
== compressors
.end())
1054 if (AutoClose
== true && Fd
!= -1)
1056 return FileFdError("Can't find a configured compressor %s for file %s", name
.c_str(), FileName
.c_str());
1058 return OpenDescriptor(Fd
, Mode
, *compressor
, AutoClose
);
1060 bool FileFd::OpenDescriptor(int Fd
, unsigned int const Mode
, APT::Configuration::Compressor
const &compressor
, bool AutoClose
)
1063 Flags
= (AutoClose
) ? FileFd::AutoClose
: 0;
1064 if (AutoClose
== false && (
1066 compressor
.Name
== "gzip" ||
1069 compressor
.Name
== "bzip2" ||
1073 // Need to duplicate fd here or gzclose for cleanup will close the fd as well
1078 this->FileName
= "";
1079 if (Fd
== -1 || OpenInternDescriptor(Mode
, compressor
) == false)
1083 compressor
.Name
== "gzip" ||
1086 compressor
.Name
== "bzip2" ||
1093 return FileFdError(_("Could not open file descriptor %d"), Fd
);
1097 bool FileFd::OpenInternDescriptor(unsigned int const Mode
, APT::Configuration::Compressor
const &compressor
)
1099 if (compressor
.Name
== "." || compressor
.Binary
.empty() == true)
1104 d
= new FileFdPrivate();
1106 d
->compressor
= compressor
;
1110 if (compressor
.Name
== "gzip")
1117 if ((Mode
& ReadWrite
) == ReadWrite
)
1118 d
->gz
= gzdopen(iFd
, "r+");
1119 else if ((Mode
& WriteOnly
) == WriteOnly
)
1120 d
->gz
= gzdopen(iFd
, "w");
1122 d
->gz
= gzdopen(iFd
, "r");
1125 Flags
|= Compressed
;
1130 if (compressor
.Name
== "bzip2")
1134 BZ2_bzclose(d
->bz2
);
1137 if ((Mode
& ReadWrite
) == ReadWrite
)
1138 d
->bz2
= BZ2_bzdopen(iFd
, "r+");
1139 else if ((Mode
& WriteOnly
) == WriteOnly
)
1140 d
->bz2
= BZ2_bzdopen(iFd
, "w");
1142 d
->bz2
= BZ2_bzdopen(iFd
, "r");
1145 Flags
|= Compressed
;
1150 // collect zombies here in case we reopen
1151 if (d
->compressor_pid
> 0)
1152 ExecWait(d
->compressor_pid
, "FileFdCompressor", true);
1154 if ((Mode
& ReadWrite
) == ReadWrite
)
1155 return FileFdError("ReadWrite mode is not supported for file %s", FileName
.c_str());
1157 bool const Comp
= (Mode
& WriteOnly
) == WriteOnly
;
1160 // Handle 'decompression' of empty files
1163 if (Buf
.st_size
== 0 && S_ISFIFO(Buf
.st_mode
) == false)
1166 // We don't need the file open - instead let the compressor open it
1167 // as he properly knows better how to efficiently read from 'his' file
1168 if (FileName
.empty() == false)
1175 // Create a data pipe
1176 int Pipe
[2] = {-1,-1};
1177 if (pipe(Pipe
) != 0)
1178 return FileFdErrno("pipe",_("Failed to create subprocess IPC"));
1179 for (int J
= 0; J
!= 2; J
++)
1180 SetCloseExec(Pipe
[J
],true);
1182 d
->compressed_fd
= iFd
;
1191 d
->compressor_pid
= ExecFork();
1192 if (d
->compressor_pid
== 0)
1196 dup2(d
->compressed_fd
,STDOUT_FILENO
);
1197 dup2(Pipe
[0],STDIN_FILENO
);
1201 if (FileName
.empty() == true)
1202 dup2(d
->compressed_fd
,STDIN_FILENO
);
1203 dup2(Pipe
[1],STDOUT_FILENO
);
1205 int const nullfd
= open("/dev/null", O_WRONLY
);
1208 dup2(nullfd
,STDERR_FILENO
);
1212 SetCloseExec(STDOUT_FILENO
,false);
1213 SetCloseExec(STDIN_FILENO
,false);
1215 std::vector
<char const*> Args
;
1216 Args
.push_back(compressor
.Binary
.c_str());
1217 std::vector
<std::string
> const * const addArgs
=
1218 (Comp
== true) ? &(compressor
.CompressArgs
) : &(compressor
.UncompressArgs
);
1219 for (std::vector
<std::string
>::const_iterator a
= addArgs
->begin();
1220 a
!= addArgs
->end(); ++a
)
1221 Args
.push_back(a
->c_str());
1222 if (Comp
== false && FileName
.empty() == false)
1224 Args
.push_back("--stdout");
1225 if (TemporaryFileName
.empty() == false)
1226 Args
.push_back(TemporaryFileName
.c_str());
1228 Args
.push_back(FileName
.c_str());
1230 Args
.push_back(NULL
);
1232 execvp(Args
[0],(char **)&Args
[0]);
1233 cerr
<< _("Failed to exec compressor ") << Args
[0] << endl
;
1244 // FileFd::~File - Closes the file /*{{{*/
1245 // ---------------------------------------------------------------------
1246 /* If the proper modes are selected then we close the Fd and possibly
1247 unlink the file on error. */
1252 d
->CloseDown(FileName
);
1257 // FileFd::Read - Read a bit of the file /*{{{*/
1258 // ---------------------------------------------------------------------
1259 /* We are carefull to handle interruption by a signal while reading
1261 bool FileFd::Read(void *To
,unsigned long long Size
,unsigned long long *Actual
)
1267 *((char *)To
) = '\0';
1271 if (d
!= NULL
&& d
->gz
!= NULL
)
1272 Res
= gzread(d
->gz
,To
,Size
);
1276 if (d
!= NULL
&& d
->bz2
!= NULL
)
1277 Res
= BZ2_bzread(d
->bz2
,To
,Size
);
1280 Res
= read(iFd
,To
,Size
);
1287 if (d
!= NULL
&& d
->gz
!= NULL
)
1290 char const * const errmsg
= gzerror(d
->gz
, &err
);
1292 return FileFdError("gzread: %s (%d: %s)", _("Read error"), err
, errmsg
);
1296 if (d
!= NULL
&& d
->bz2
!= NULL
)
1299 char const * const errmsg
= BZ2_bzerror(d
->bz2
, &err
);
1300 if (err
!= BZ_IO_ERROR
)
1301 return FileFdError("BZ2_bzread: %s (%d: %s)", _("Read error"), err
, errmsg
);
1304 return FileFdErrno("read",_("Read error"));
1307 To
= (char *)To
+ Res
;
1314 while (Res
> 0 && Size
> 0);
1326 return FileFdError(_("read, still have %llu to read but none left"), Size
);
1329 // FileFd::ReadLine - Read a complete line from the file /*{{{*/
1330 // ---------------------------------------------------------------------
1331 /* Beware: This method can be quiet slow for big buffers on UNcompressed
1332 files because of the naive implementation! */
1333 char* FileFd::ReadLine(char *To
, unsigned long long const Size
)
1337 if (d
!= NULL
&& d
->gz
!= NULL
)
1338 return gzgets(d
->gz
, To
, Size
);
1341 unsigned long long read
= 0;
1342 while ((Size
- 1) != read
)
1344 unsigned long long done
= 0;
1345 if (Read(To
+ read
, 1, &done
) == false)
1349 if (To
[read
++] == '\n')
1358 // FileFd::Write - Write to the file /*{{{*/
1359 // ---------------------------------------------------------------------
1361 bool FileFd::Write(const void *From
,unsigned long long Size
)
1368 if (d
!= NULL
&& d
->gz
!= NULL
)
1369 Res
= gzwrite(d
->gz
,From
,Size
);
1373 if (d
!= NULL
&& d
->bz2
!= NULL
)
1374 Res
= BZ2_bzwrite(d
->bz2
,(void*)From
,Size
);
1377 Res
= write(iFd
,From
,Size
);
1378 if (Res
< 0 && errno
== EINTR
)
1383 if (d
!= NULL
&& d
->gz
!= NULL
)
1386 char const * const errmsg
= gzerror(d
->gz
, &err
);
1388 return FileFdError("gzwrite: %s (%d: %s)", _("Write error"), err
, errmsg
);
1392 if (d
!= NULL
&& d
->bz2
!= NULL
)
1395 char const * const errmsg
= BZ2_bzerror(d
->bz2
, &err
);
1396 if (err
!= BZ_IO_ERROR
)
1397 return FileFdError("BZ2_bzwrite: %s (%d: %s)", _("Write error"), err
, errmsg
);
1400 return FileFdErrno("write",_("Write error"));
1403 From
= (char *)From
+ Res
;
1408 while (Res
> 0 && Size
> 0);
1413 return FileFdError(_("write, still have %llu to write but couldn't"), Size
);
1415 bool FileFd::Write(int Fd
, const void *From
, unsigned long long Size
)
1421 Res
= write(Fd
,From
,Size
);
1422 if (Res
< 0 && errno
== EINTR
)
1425 return _error
->Errno("write",_("Write error"));
1427 From
= (char *)From
+ Res
;
1430 while (Res
> 0 && Size
> 0);
1435 return _error
->Error(_("write, still have %llu to write but couldn't"), Size
);
1438 // FileFd::Seek - Seek in the file /*{{{*/
1439 // ---------------------------------------------------------------------
1441 bool FileFd::Seek(unsigned long long To
)
1443 if (d
!= NULL
&& (d
->pipe
== true
1449 // Our poor man seeking in pipes is costly, so try to avoid it
1450 unsigned long long seekpos
= Tell();
1453 else if (seekpos
< To
)
1454 return Skip(To
- seekpos
);
1456 if ((d
->openmode
& ReadOnly
) != ReadOnly
)
1457 return FileFdError("Reopen is only implemented for read-only files!");
1461 BZ2_bzclose(d
->bz2
);
1468 if (TemporaryFileName
.empty() == false)
1469 iFd
= open(TemporaryFileName
.c_str(), O_RDONLY
);
1470 else if (FileName
.empty() == false)
1471 iFd
= open(FileName
.c_str(), O_RDONLY
);
1474 if (d
->compressed_fd
> 0)
1475 if (lseek(d
->compressed_fd
, 0, SEEK_SET
) != 0)
1476 iFd
= d
->compressed_fd
;
1478 return FileFdError("Reopen is not implemented for pipes opened with FileFd::OpenDescriptor()!");
1481 if (OpenInternDescriptor(d
->openmode
, d
->compressor
) == false)
1482 return FileFdError("Seek on file %s because it couldn't be reopened", FileName
.c_str());
1492 if (d
!= NULL
&& d
->gz
)
1493 res
= gzseek(d
->gz
,To
,SEEK_SET
);
1496 res
= lseek(iFd
,To
,SEEK_SET
);
1497 if (res
!= (off_t
)To
)
1498 return FileFdError("Unable to seek to %llu", To
);
1505 // FileFd::Skip - Seek in the file /*{{{*/
1506 // ---------------------------------------------------------------------
1508 bool FileFd::Skip(unsigned long long Over
)
1510 if (d
!= NULL
&& (d
->pipe
== true
1520 unsigned long long toread
= std::min((unsigned long long) sizeof(buffer
), Over
);
1521 if (Read(buffer
, toread
) == false)
1522 return FileFdError("Unable to seek ahead %llu",Over
);
1530 if (d
!= NULL
&& d
->gz
!= NULL
)
1531 res
= gzseek(d
->gz
,Over
,SEEK_CUR
);
1534 res
= lseek(iFd
,Over
,SEEK_CUR
);
1536 return FileFdError("Unable to seek ahead %llu",Over
);
1543 // FileFd::Truncate - Truncate the file /*{{{*/
1544 // ---------------------------------------------------------------------
1546 bool FileFd::Truncate(unsigned long long To
)
1548 // truncating /dev/null is always successful - as we get an error otherwise
1549 if (To
== 0 && FileName
== "/dev/null")
1551 #if defined HAVE_ZLIB || defined HAVE_BZ2
1552 if (d
!= NULL
&& (d
->gz
!= NULL
|| d
->bz2
!= NULL
))
1553 return FileFdError("Truncating compressed files is not implemented (%s)", FileName
.c_str());
1555 if (ftruncate(iFd
,To
) != 0)
1556 return FileFdError("Unable to truncate to %llu",To
);
1561 // FileFd::Tell - Current seek position /*{{{*/
1562 // ---------------------------------------------------------------------
1564 unsigned long long FileFd::Tell()
1566 // In theory, we could just return seekpos here always instead of
1567 // seeking around, but not all users of FileFd use always Seek() and co
1568 // so d->seekpos isn't always true and we can just use it as a hint if
1569 // we have nothing else, but not always as an authority…
1570 if (d
!= NULL
&& (d
->pipe
== true
1579 if (d
!= NULL
&& d
->gz
!= NULL
)
1580 Res
= gztell(d
->gz
);
1583 Res
= lseek(iFd
,0,SEEK_CUR
);
1584 if (Res
== (off_t
)-1)
1585 FileFdErrno("lseek","Failed to determine the current file position");
1591 // FileFd::FileSize - Return the size of the file /*{{{*/
1592 // ---------------------------------------------------------------------
1594 unsigned long long FileFd::FileSize()
1597 if ((d
== NULL
|| d
->pipe
== false) && fstat(iFd
,&Buf
) != 0)
1598 return FileFdErrno("fstat","Unable to determine the file size");
1600 // for compressor pipes st_size is undefined and at 'best' zero
1601 if ((d
!= NULL
&& d
->pipe
== true) || S_ISFIFO(Buf
.st_mode
))
1603 // we set it here, too, as we get the info here for free
1604 // in theory the Open-methods should take care of it already
1607 if (stat(FileName
.c_str(), &Buf
) != 0)
1608 return FileFdErrno("stat","Unable to determine the file size");
1614 // FileFd::Size - Return the size of the content in the file /*{{{*/
1615 // ---------------------------------------------------------------------
1617 unsigned long long FileFd::Size()
1619 unsigned long long size
= FileSize();
1621 // for compressor pipes st_size is undefined and at 'best' zero,
1622 // so we 'read' the content and 'seek' back - see there
1623 if (d
!= NULL
&& (d
->pipe
== true
1625 || (d
->bz2
&& size
> 0)
1629 unsigned long long const oldSeek
= Tell();
1631 unsigned long long read
= 0;
1633 if (Read(ignore
, sizeof(ignore
), &read
) == false)
1643 // only check gzsize if we are actually a gzip file, just checking for
1644 // "gz" is not sufficient as uncompressed files could be opened with
1645 // gzopen in "direct" mode as well
1646 else if (d
!= NULL
&& d
->gz
&& !gzdirect(d
->gz
) && size
> 0)
1648 off_t
const oldPos
= lseek(iFd
,0,SEEK_CUR
);
1649 /* unfortunately zlib.h doesn't provide a gzsize(), so we have to do
1650 * this ourselves; the original (uncompressed) file size is the last 32
1651 * bits of the file */
1652 // FIXME: Size for gz-files is limited by 32bit… no largefile support
1653 if (lseek(iFd
, -4, SEEK_END
) < 0)
1655 FileFdErrno("lseek","Unable to seek to end of gzipped file");
1659 if (read(iFd
, &size
, 4) != 4)
1661 FileFdErrno("read","Unable to read original size of gzipped file");
1665 #ifdef WORDS_BIGENDIAN
1666 uint32_t tmp_size
= size
;
1667 uint8_t const * const p
= (uint8_t const * const) &tmp_size
;
1668 tmp_size
= (p
[3] << 24) | (p
[2] << 16) | (p
[1] << 8) | p
[0];
1672 if (lseek(iFd
, oldPos
, SEEK_SET
) < 0)
1674 FileFdErrno("lseek","Unable to seek in gzipped file");
1685 // FileFd::ModificationTime - Return the time of last touch /*{{{*/
1686 // ---------------------------------------------------------------------
1688 time_t FileFd::ModificationTime()
1691 if ((d
== NULL
|| d
->pipe
== false) && fstat(iFd
,&Buf
) != 0)
1693 FileFdErrno("fstat","Unable to determine the modification time of file %s", FileName
.c_str());
1697 // for compressor pipes st_size is undefined and at 'best' zero
1698 if ((d
!= NULL
&& d
->pipe
== true) || S_ISFIFO(Buf
.st_mode
))
1700 // we set it here, too, as we get the info here for free
1701 // in theory the Open-methods should take care of it already
1704 if (stat(FileName
.c_str(), &Buf
) != 0)
1706 FileFdErrno("fstat","Unable to determine the modification time of file %s", FileName
.c_str());
1711 return Buf
.st_mtime
;
1714 // FileFd::Close - Close the file if the close flag is set /*{{{*/
1715 // ---------------------------------------------------------------------
1717 bool FileFd::Close()
1723 if ((Flags
& AutoClose
) == AutoClose
)
1725 if ((Flags
& Compressed
) != Compressed
&& iFd
> 0 && close(iFd
) != 0)
1726 Res
&= _error
->Errno("close",_("Problem closing the file %s"), FileName
.c_str());
1730 Res
&= d
->CloseDown(FileName
);
1736 if ((Flags
& Replace
) == Replace
) {
1737 if (rename(TemporaryFileName
.c_str(), FileName
.c_str()) != 0)
1738 Res
&= _error
->Errno("rename",_("Problem renaming the file %s to %s"), TemporaryFileName
.c_str(), FileName
.c_str());
1740 FileName
= TemporaryFileName
; // for the unlink() below.
1741 TemporaryFileName
.clear();
1746 if ((Flags
& Fail
) == Fail
&& (Flags
& DelOnFail
) == DelOnFail
&&
1747 FileName
.empty() == false)
1748 if (unlink(FileName
.c_str()) != 0)
1749 Res
&= _error
->WarningE("unlnk",_("Problem unlinking the file %s"), FileName
.c_str());
1756 // FileFd::Sync - Sync the file /*{{{*/
1757 // ---------------------------------------------------------------------
1761 if (fsync(iFd
) != 0)
1762 return FileFdErrno("sync",_("Problem syncing the file"));
1766 // FileFd::FileFdErrno - set Fail and call _error->Errno *{{{*/
1767 bool FileFd::FileFdErrno(const char *Function
, const char *Description
,...)
1771 size_t msgSize
= 400;
1772 int const errsv
= errno
;
1775 va_start(args
,Description
);
1776 if (_error
->InsertErrno(GlobalError::ERROR
, Function
, Description
, args
, errsv
, msgSize
) == false)
1783 // FileFd::FileFdError - set Fail and call _error->Error *{{{*/
1784 bool FileFd::FileFdError(const char *Description
,...) {
1787 size_t msgSize
= 400;
1790 va_start(args
,Description
);
1791 if (_error
->Insert(GlobalError::ERROR
, Description
, args
, msgSize
) == false)
1799 gzFile
FileFd::gzFd() { return (gzFile
) d
->gz
; }
1802 // Glob - wrapper around "glob()" /*{{{*/
1803 // ---------------------------------------------------------------------
1805 std::vector
<std::string
> Glob(std::string
const &pattern
, int flags
)
1807 std::vector
<std::string
> result
;
1812 glob_res
= glob(pattern
.c_str(), flags
, NULL
, &globbuf
);
1816 if(glob_res
!= GLOB_NOMATCH
) {
1817 _error
->Errno("glob", "Problem with glob");
1823 for(i
=0;i
<globbuf
.gl_pathc
;i
++)
1824 result
.push_back(string(globbuf
.gl_pathv
[i
]));