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>
29 #include <apt-pkg/macros.h>
34 #include <sys/select.h>
62 #ifdef WORDS_BIGENDIAN
86 APT::Configuration::Compressor compressor
;
87 unsigned int openmode
;
88 unsigned long long seekpos
;
89 FileFdPrivate() : gz(NULL
), bz2(NULL
),
90 compressed_fd(-1), compressor_pid(-1), pipe(false),
91 openmode(0), seekpos(0) {};
92 bool CloseDown(std::string
const &FileName
)
97 int const e
= gzclose(gz
);
99 // gzdclose() on empty files always fails with "buffer error" here, ignore that
100 if (e
!= 0 && e
!= Z_BUF_ERROR
)
101 Res
&= _error
->Errno("close",_("Problem closing the gzip file %s"), FileName
.c_str());
110 if (compressor_pid
> 0)
111 ExecWait(compressor_pid
, "FileFdCompressor", true);
116 ~FileFdPrivate() { CloseDown(""); }
119 // RunScripts - Run a set of scripts from a configuration subtree /*{{{*/
120 // ---------------------------------------------------------------------
122 bool RunScripts(const char *Cnf
)
124 Configuration::Item
const *Opts
= _config
->Tree(Cnf
);
125 if (Opts
== 0 || Opts
->Child
== 0)
129 // Fork for running the system calls
130 pid_t Child
= ExecFork();
135 if (_config
->FindDir("DPkg::Chroot-Directory","/") != "/")
137 std::cerr
<< "Chrooting into "
138 << _config
->FindDir("DPkg::Chroot-Directory")
140 if (chroot(_config
->FindDir("DPkg::Chroot-Directory","/").c_str()) != 0)
144 if (chdir("/tmp/") != 0)
147 unsigned int Count
= 1;
148 for (; Opts
!= 0; Opts
= Opts
->Next
, Count
++)
150 if (Opts
->Value
.empty() == true)
153 if (system(Opts
->Value
.c_str()) != 0)
159 // Wait for the child
161 while (waitpid(Child
,&Status
,0) != Child
)
165 return _error
->Errno("waitpid","Couldn't wait for subprocess");
168 // Restore sig int/quit
169 signal(SIGQUIT
,SIG_DFL
);
170 signal(SIGINT
,SIG_DFL
);
172 // Check for an error code.
173 if (WIFEXITED(Status
) == 0 || WEXITSTATUS(Status
) != 0)
175 unsigned int Count
= WEXITSTATUS(Status
);
179 for (; Opts
!= 0 && Count
!= 1; Opts
= Opts
->Next
, Count
--);
180 _error
->Error("Problem executing scripts %s '%s'",Cnf
,Opts
->Value
.c_str());
183 return _error
->Error("Sub-process returned an error code");
190 // CopyFile - Buffered copy of a file /*{{{*/
191 // ---------------------------------------------------------------------
192 /* The caller is expected to set things so that failure causes erasure */
193 bool CopyFile(FileFd
&From
,FileFd
&To
)
195 if (From
.IsOpen() == false || To
.IsOpen() == false ||
196 From
.Failed() == true || To
.Failed() == true)
199 // Buffered copy between fds
200 SPtrArray
<unsigned char> Buf
= new unsigned char[64000];
201 unsigned long long Size
= From
.Size();
204 unsigned long long ToRead
= Size
;
208 if (From
.Read(Buf
,ToRead
) == false ||
209 To
.Write(Buf
,ToRead
) == false)
218 // GetLock - Gets a lock file /*{{{*/
219 // ---------------------------------------------------------------------
220 /* This will create an empty file of the given name and lock it. Once this
221 is done all other calls to GetLock in any other process will fail with
222 -1. The return result is the fd of the file, the call should call
223 close at some time. */
224 int GetLock(string File
,bool Errors
)
226 // GetLock() is used in aptitude on directories with public-write access
227 // Use O_NOFOLLOW here to prevent symlink traversal attacks
228 int FD
= open(File
.c_str(),O_RDWR
| O_CREAT
| O_NOFOLLOW
,0640);
231 // Read only .. can't have locking problems there.
234 _error
->Warning(_("Not using locking for read only lock file %s"),File
.c_str());
235 return dup(0); // Need something for the caller to close
239 _error
->Errno("open",_("Could not open lock file %s"),File
.c_str());
241 // Feh.. We do this to distinguish the lock vs open case..
245 SetCloseExec(FD
,true);
247 // Acquire a write lock
250 fl
.l_whence
= SEEK_SET
;
253 if (fcntl(FD
,F_SETLK
,&fl
) == -1)
255 // always close to not leak resources
262 _error
->Warning(_("Not using locking for nfs mounted lock file %s"),File
.c_str());
263 return dup(0); // Need something for the caller to close
267 _error
->Errno("open",_("Could not get lock %s"),File
.c_str());
275 // FileExists - Check if a file exists /*{{{*/
276 // ---------------------------------------------------------------------
277 /* Beware: Directories are also files! */
278 bool FileExists(string File
)
281 if (stat(File
.c_str(),&Buf
) != 0)
286 // RealFileExists - Check if a file exists and if it is really a file /*{{{*/
287 // ---------------------------------------------------------------------
289 bool RealFileExists(string File
)
292 if (stat(File
.c_str(),&Buf
) != 0)
294 return ((Buf
.st_mode
& S_IFREG
) != 0);
297 // DirectoryExists - Check if a directory exists and is really one /*{{{*/
298 // ---------------------------------------------------------------------
300 bool DirectoryExists(string
const &Path
)
303 if (stat(Path
.c_str(),&Buf
) != 0)
305 return ((Buf
.st_mode
& S_IFDIR
) != 0);
308 // CreateDirectory - poor man's mkdir -p guarded by a parent directory /*{{{*/
309 // ---------------------------------------------------------------------
310 /* This method will create all directories needed for path in good old
311 mkdir -p style but refuses to do this if Parent is not a prefix of
312 this Path. Example: /var/cache/ and /var/cache/apt/archives are given,
313 so it will create apt/archives if /var/cache exists - on the other
314 hand if the parent is /var/lib the creation will fail as this path
315 is not a parent of the path to be generated. */
316 bool CreateDirectory(string
const &Parent
, string
const &Path
)
318 if (Parent
.empty() == true || Path
.empty() == true)
321 if (DirectoryExists(Path
) == true)
324 if (DirectoryExists(Parent
) == false)
327 // we are not going to create directories "into the blue"
328 if (Path
.compare(0, Parent
.length(), Parent
) != 0)
331 vector
<string
> const dirs
= VectorizeString(Path
.substr(Parent
.size()), '/');
332 string progress
= Parent
;
333 for (vector
<string
>::const_iterator d
= dirs
.begin(); d
!= dirs
.end(); ++d
)
335 if (d
->empty() == true)
338 progress
.append("/").append(*d
);
339 if (DirectoryExists(progress
) == true)
342 if (mkdir(progress
.c_str(), 0755) != 0)
348 // CreateAPTDirectoryIfNeeded - ensure that the given directory exists /*{{{*/
349 // ---------------------------------------------------------------------
350 /* a small wrapper around CreateDirectory to check if it exists and to
351 remove the trailing "/apt/" from the parent directory if needed */
352 bool CreateAPTDirectoryIfNeeded(string
const &Parent
, string
const &Path
)
354 if (DirectoryExists(Path
) == true)
357 size_t const len
= Parent
.size();
358 if (len
> 5 && Parent
.find("/apt/", len
- 6, 5) == len
- 5)
360 if (CreateDirectory(Parent
.substr(0,len
-5), Path
) == true)
363 else if (CreateDirectory(Parent
, Path
) == true)
369 // GetListOfFilesInDir - returns a vector of files in the given dir /*{{{*/
370 // ---------------------------------------------------------------------
371 /* If an extension is given only files with this extension are included
372 in the returned vector, otherwise every "normal" file is included. */
373 std::vector
<string
> GetListOfFilesInDir(string
const &Dir
, string
const &Ext
,
374 bool const &SortList
, bool const &AllowNoExt
)
376 std::vector
<string
> ext
;
378 if (Ext
.empty() == false)
380 if (AllowNoExt
== true && ext
.empty() == false)
382 return GetListOfFilesInDir(Dir
, ext
, SortList
);
384 std::vector
<string
> GetListOfFilesInDir(string
const &Dir
, std::vector
<string
> const &Ext
,
385 bool const &SortList
)
387 // Attention debuggers: need to be set with the environment config file!
388 bool const Debug
= _config
->FindB("Debug::GetListOfFilesInDir", false);
391 std::clog
<< "Accept in " << Dir
<< " only files with the following " << Ext
.size() << " extensions:" << std::endl
;
392 if (Ext
.empty() == true)
393 std::clog
<< "\tNO extension" << std::endl
;
395 for (std::vector
<string
>::const_iterator e
= Ext
.begin();
397 std::clog
<< '\t' << (e
->empty() == true ? "NO" : *e
) << " extension" << std::endl
;
400 std::vector
<string
> List
;
402 if (DirectoryExists(Dir
) == false)
404 _error
->Error(_("List of files can't be created as '%s' is not a directory"), Dir
.c_str());
408 Configuration::MatchAgainstConfig
SilentIgnore("Dir::Ignore-Files-Silently");
409 DIR *D
= opendir(Dir
.c_str());
412 _error
->Errno("opendir",_("Unable to read %s"),Dir
.c_str());
416 for (struct dirent
*Ent
= readdir(D
); Ent
!= 0; Ent
= readdir(D
))
418 // skip "hidden" files
419 if (Ent
->d_name
[0] == '.')
422 // Make sure it is a file and not something else
423 string
const File
= flCombine(Dir
,Ent
->d_name
);
424 #ifdef _DIRENT_HAVE_D_TYPE
425 if (Ent
->d_type
!= DT_REG
)
428 if (RealFileExists(File
) == false)
430 // do not show ignoration warnings for directories
432 #ifdef _DIRENT_HAVE_D_TYPE
433 Ent
->d_type
== DT_DIR
||
435 DirectoryExists(File
) == true)
437 if (SilentIgnore
.Match(Ent
->d_name
) == false)
438 _error
->Notice(_("Ignoring '%s' in directory '%s' as it is not a regular file"), Ent
->d_name
, Dir
.c_str());
443 // check for accepted extension:
444 // no extension given -> periods are bad as hell!
445 // extensions given -> "" extension allows no extension
446 if (Ext
.empty() == false)
448 string d_ext
= flExtension(Ent
->d_name
);
449 if (d_ext
== Ent
->d_name
) // no extension
451 if (std::find(Ext
.begin(), Ext
.end(), "") == Ext
.end())
454 std::clog
<< "Bad file: " << Ent
->d_name
<< " → no extension" << std::endl
;
455 if (SilentIgnore
.Match(Ent
->d_name
) == false)
456 _error
->Notice(_("Ignoring file '%s' in directory '%s' as it has no filename extension"), Ent
->d_name
, Dir
.c_str());
460 else if (std::find(Ext
.begin(), Ext
.end(), d_ext
) == Ext
.end())
463 std::clog
<< "Bad file: " << Ent
->d_name
<< " → bad extension »" << flExtension(Ent
->d_name
) << "«" << std::endl
;
464 if (SilentIgnore
.Match(Ent
->d_name
) == false)
465 _error
->Notice(_("Ignoring file '%s' in directory '%s' as it has an invalid filename extension"), Ent
->d_name
, Dir
.c_str());
470 // Skip bad filenames ala run-parts
471 const char *C
= Ent
->d_name
;
473 if (isalpha(*C
) == 0 && isdigit(*C
) == 0
474 && *C
!= '_' && *C
!= '-' && *C
!= ':') {
475 // no required extension -> dot is a bad character
476 if (*C
== '.' && Ext
.empty() == false)
481 // we don't reach the end of the name -> bad character included
485 std::clog
<< "Bad file: " << Ent
->d_name
<< " → bad character »"
486 << *C
<< "« in filename (period allowed: " << (Ext
.empty() ? "no" : "yes") << ")" << std::endl
;
490 // skip filenames which end with a period. These are never valid
494 std::clog
<< "Bad file: " << Ent
->d_name
<< " → Period as last character" << std::endl
;
499 std::clog
<< "Accept file: " << Ent
->d_name
<< " in " << Dir
<< std::endl
;
500 List
.push_back(File
);
504 if (SortList
== true)
505 std::sort(List
.begin(),List
.end());
508 std::vector
<string
> GetListOfFilesInDir(string
const &Dir
, bool SortList
)
510 bool const Debug
= _config
->FindB("Debug::GetListOfFilesInDir", false);
512 std::clog
<< "Accept in " << Dir
<< " all regular files" << std::endl
;
514 std::vector
<string
> List
;
516 if (DirectoryExists(Dir
) == false)
518 _error
->Error(_("List of files can't be created as '%s' is not a directory"), Dir
.c_str());
522 DIR *D
= opendir(Dir
.c_str());
525 _error
->Errno("opendir",_("Unable to read %s"),Dir
.c_str());
529 for (struct dirent
*Ent
= readdir(D
); Ent
!= 0; Ent
= readdir(D
))
531 // skip "hidden" files
532 if (Ent
->d_name
[0] == '.')
535 // Make sure it is a file and not something else
536 string
const File
= flCombine(Dir
,Ent
->d_name
);
537 #ifdef _DIRENT_HAVE_D_TYPE
538 if (Ent
->d_type
!= DT_REG
)
541 if (RealFileExists(File
) == false)
544 std::clog
<< "Bad file: " << Ent
->d_name
<< " → it is not a real file" << std::endl
;
549 // Skip bad filenames ala run-parts
550 const char *C
= Ent
->d_name
;
552 if (isalpha(*C
) == 0 && isdigit(*C
) == 0
553 && *C
!= '_' && *C
!= '-' && *C
!= '.')
556 // we don't reach the end of the name -> bad character included
560 std::clog
<< "Bad file: " << Ent
->d_name
<< " → bad character »" << *C
<< "« in filename" << std::endl
;
564 // skip filenames which end with a period. These are never valid
568 std::clog
<< "Bad file: " << Ent
->d_name
<< " → Period as last character" << std::endl
;
573 std::clog
<< "Accept file: " << Ent
->d_name
<< " in " << Dir
<< std::endl
;
574 List
.push_back(File
);
578 if (SortList
== true)
579 std::sort(List
.begin(),List
.end());
583 // SafeGetCWD - This is a safer getcwd that returns a dynamic string /*{{{*/
584 // ---------------------------------------------------------------------
585 /* We return / on failure. */
588 // Stash the current dir.
591 if (getcwd(S
,sizeof(S
)-2) == 0)
593 unsigned int Len
= strlen(S
);
599 // GetModificationTime - Get the mtime of the given file or -1 on error /*{{{*/
600 // ---------------------------------------------------------------------
601 /* We return / on failure. */
602 time_t GetModificationTime(string
const &Path
)
605 if (stat(Path
.c_str(), &St
) < 0)
610 // flNotDir - Strip the directory from the filename /*{{{*/
611 // ---------------------------------------------------------------------
613 string
flNotDir(string File
)
615 string::size_type Res
= File
.rfind('/');
616 if (Res
== string::npos
)
619 return string(File
,Res
,Res
- File
.length());
622 // flNotFile - Strip the file from the directory name /*{{{*/
623 // ---------------------------------------------------------------------
624 /* Result ends in a / */
625 string
flNotFile(string File
)
627 string::size_type Res
= File
.rfind('/');
628 if (Res
== string::npos
)
631 return string(File
,0,Res
);
634 // flExtension - Return the extension for the file /*{{{*/
635 // ---------------------------------------------------------------------
637 string
flExtension(string File
)
639 string::size_type Res
= File
.rfind('.');
640 if (Res
== string::npos
)
643 return string(File
,Res
,Res
- File
.length());
646 // flNoLink - If file is a symlink then deref it /*{{{*/
647 // ---------------------------------------------------------------------
648 /* If the name is not a link then the returned path is the input. */
649 string
flNoLink(string File
)
652 if (lstat(File
.c_str(),&St
) != 0 || S_ISLNK(St
.st_mode
) == 0)
654 if (stat(File
.c_str(),&St
) != 0)
657 /* Loop resolving the link. There is no need to limit the number of
658 loops because the stat call above ensures that the symlink is not
666 if ((Res
= readlink(NFile
.c_str(),Buffer
,sizeof(Buffer
))) <= 0 ||
667 (size_t)Res
>= sizeof(Buffer
))
670 // Append or replace the previous path
672 if (Buffer
[0] == '/')
675 NFile
= flNotFile(NFile
) + Buffer
;
677 // See if we are done
678 if (lstat(NFile
.c_str(),&St
) != 0)
680 if (S_ISLNK(St
.st_mode
) == 0)
685 // flCombine - Combine a file and a directory /*{{{*/
686 // ---------------------------------------------------------------------
687 /* If the file is an absolute path then it is just returned, otherwise
688 the directory is pre-pended to it. */
689 string
flCombine(string Dir
,string File
)
691 if (File
.empty() == true)
694 if (File
[0] == '/' || Dir
.empty() == true)
696 if (File
.length() >= 2 && File
[0] == '.' && File
[1] == '/')
698 if (Dir
[Dir
.length()-1] == '/')
700 return Dir
+ '/' + File
;
703 // SetCloseExec - Set the close on exec flag /*{{{*/
704 // ---------------------------------------------------------------------
706 void SetCloseExec(int Fd
,bool Close
)
708 if (fcntl(Fd
,F_SETFD
,(Close
== false)?0:FD_CLOEXEC
) != 0)
710 cerr
<< "FATAL -> Could not set close on exec " << strerror(errno
) << endl
;
715 // SetNonBlock - Set the nonblocking flag /*{{{*/
716 // ---------------------------------------------------------------------
718 void SetNonBlock(int Fd
,bool Block
)
720 int Flags
= fcntl(Fd
,F_GETFL
) & (~O_NONBLOCK
);
721 if (fcntl(Fd
,F_SETFL
,Flags
| ((Block
== false)?0:O_NONBLOCK
)) != 0)
723 cerr
<< "FATAL -> Could not set non-blocking flag " << strerror(errno
) << endl
;
728 // WaitFd - Wait for a FD to become readable /*{{{*/
729 // ---------------------------------------------------------------------
730 /* This waits for a FD to become readable using select. It is useful for
731 applications making use of non-blocking sockets. The timeout is
733 bool WaitFd(int Fd
,bool write
,unsigned long timeout
)
746 Res
= select(Fd
+1,0,&Set
,0,(timeout
!= 0?&tv
:0));
748 while (Res
< 0 && errno
== EINTR
);
758 Res
= select(Fd
+1,&Set
,0,0,(timeout
!= 0?&tv
:0));
760 while (Res
< 0 && errno
== EINTR
);
769 // MergeKeepFdsFromConfiguration - Merge APT::Keep-Fds configuration /*{{{*/
770 // ---------------------------------------------------------------------
771 /* This is used to merge the APT::Keep-Fds with the provided KeepFDs
774 void MergeKeepFdsFromConfiguration(std::set
<int> &KeepFDs
)
776 Configuration::Item
const *Opts
= _config
->Tree("APT::Keep-Fds");
777 if (Opts
!= 0 && Opts
->Child
!= 0)
780 for (; Opts
!= 0; Opts
= Opts
->Next
)
782 if (Opts
->Value
.empty() == true)
784 int fd
= atoi(Opts
->Value
.c_str());
790 // ExecFork - Magical fork that sanitizes the context before execing /*{{{*/
791 // ---------------------------------------------------------------------
792 /* This is used if you want to cleanse the environment for the forked
793 child, it fixes up the important signals and nukes all of the fds,
794 otherwise acts like normal fork. */
798 // we need to merge the Keep-Fds as external tools like
799 // debconf-apt-progress use it
800 MergeKeepFdsFromConfiguration(KeepFDs
);
801 return ExecFork(KeepFDs
);
804 pid_t
ExecFork(std::set
<int> KeepFDs
)
806 // Fork off the process
807 pid_t Process
= fork();
810 cerr
<< "FATAL -> Failed to fork." << endl
;
814 // Spawn the subprocess
818 signal(SIGPIPE
,SIG_DFL
);
819 signal(SIGQUIT
,SIG_DFL
);
820 signal(SIGINT
,SIG_DFL
);
821 signal(SIGWINCH
,SIG_DFL
);
822 signal(SIGCONT
,SIG_DFL
);
823 signal(SIGTSTP
,SIG_DFL
);
825 // Close all of our FDs - just in case
826 for (int K
= 3; K
!= sysconf(_SC_OPEN_MAX
); K
++)
828 if(KeepFDs
.find(K
) == KeepFDs
.end())
829 fcntl(K
,F_SETFD
,FD_CLOEXEC
);
836 // ExecWait - Fancy waitpid /*{{{*/
837 // ---------------------------------------------------------------------
838 /* Waits for the given sub process. If Reap is set then no errors are
839 generated. Otherwise a failed subprocess will generate a proper descriptive
841 bool ExecWait(pid_t Pid
,const char *Name
,bool Reap
)
846 // Wait and collect the error code
848 while (waitpid(Pid
,&Status
,0) != Pid
)
856 return _error
->Error(_("Waited for %s but it wasn't there"),Name
);
860 // Check for an error code.
861 if (WIFEXITED(Status
) == 0 || WEXITSTATUS(Status
) != 0)
865 if (WIFSIGNALED(Status
) != 0)
867 if( WTERMSIG(Status
) == SIGSEGV
)
868 return _error
->Error(_("Sub-process %s received a segmentation fault."),Name
);
870 return _error
->Error(_("Sub-process %s received signal %u."),Name
, WTERMSIG(Status
));
873 if (WIFEXITED(Status
) != 0)
874 return _error
->Error(_("Sub-process %s returned an error code (%u)"),Name
,WEXITSTATUS(Status
));
876 return _error
->Error(_("Sub-process %s exited unexpectedly"),Name
);
883 // FileFd::Open - Open a file /*{{{*/
884 // ---------------------------------------------------------------------
885 /* The most commonly used open mode combinations are given with Mode */
886 bool FileFd::Open(string FileName
,unsigned int const Mode
,CompressMode Compress
, unsigned long const Perms
)
888 if (Mode
== ReadOnlyGzip
)
889 return Open(FileName
, ReadOnly
, Gzip
, Perms
);
891 if (Compress
== Auto
&& (Mode
& WriteOnly
) == WriteOnly
)
892 return FileFdError("Autodetection on %s only works in ReadOnly openmode!", FileName
.c_str());
894 std::vector
<APT::Configuration::Compressor
> const compressors
= APT::Configuration::getCompressors();
895 std::vector
<APT::Configuration::Compressor
>::const_iterator compressor
= compressors
.begin();
896 if (Compress
== Auto
)
898 for (; compressor
!= compressors
.end(); ++compressor
)
900 std::string file
= FileName
+ compressor
->Extension
;
901 if (FileExists(file
) == false)
907 else if (Compress
== Extension
)
909 std::string::size_type
const found
= FileName
.find_last_of('.');
911 if (found
!= std::string::npos
)
913 ext
= FileName
.substr(found
);
914 if (ext
== ".new" || ext
== ".bak")
916 std::string::size_type
const found2
= FileName
.find_last_of('.', found
- 1);
917 if (found2
!= std::string::npos
)
918 ext
= FileName
.substr(found2
, found
- found2
);
923 for (; compressor
!= compressors
.end(); ++compressor
)
924 if (ext
== compressor
->Extension
)
926 // no matching extension - assume uncompressed (imagine files like 'example.org_Packages')
927 if (compressor
== compressors
.end())
928 for (compressor
= compressors
.begin(); compressor
!= compressors
.end(); ++compressor
)
929 if (compressor
->Name
== ".")
937 case None
: name
= "."; break;
938 case Gzip
: name
= "gzip"; break;
939 case Bzip2
: name
= "bzip2"; break;
940 case Lzma
: name
= "lzma"; break;
941 case Xz
: name
= "xz"; break;
945 return FileFdError("Opening File %s in None, Auto or Extension should be already handled?!?", FileName
.c_str());
947 for (; compressor
!= compressors
.end(); ++compressor
)
948 if (compressor
->Name
== name
)
950 if (compressor
== compressors
.end())
951 return FileFdError("Can't find a configured compressor %s for file %s", name
.c_str(), FileName
.c_str());
954 if (compressor
== compressors
.end())
955 return FileFdError("Can't find a match for specified compressor mode for file %s", FileName
.c_str());
956 return Open(FileName
, Mode
, *compressor
, Perms
);
958 bool FileFd::Open(string FileName
,unsigned int const Mode
,APT::Configuration::Compressor
const &compressor
, unsigned long const Perms
)
963 if ((Mode
& WriteOnly
) != WriteOnly
&& (Mode
& (Atomic
| Create
| Empty
| Exclusive
)) != 0)
964 return FileFdError("ReadOnly mode for %s doesn't accept additional flags!", FileName
.c_str());
965 if ((Mode
& ReadWrite
) == 0)
966 return FileFdError("No openmode provided in FileFd::Open for %s", FileName
.c_str());
968 if ((Mode
& Atomic
) == Atomic
)
972 else if ((Mode
& (Exclusive
| Create
)) == (Exclusive
| Create
))
974 // for atomic, this will be done by rename in Close()
975 unlink(FileName
.c_str());
977 if ((Mode
& Empty
) == Empty
)
980 if (lstat(FileName
.c_str(),&Buf
) == 0 && S_ISLNK(Buf
.st_mode
))
981 unlink(FileName
.c_str());
985 #define if_FLAGGED_SET(FLAG, MODE) if ((Mode & FLAG) == FLAG) fileflags |= MODE
986 if_FLAGGED_SET(ReadWrite
, O_RDWR
);
987 else if_FLAGGED_SET(ReadOnly
, O_RDONLY
);
988 else if_FLAGGED_SET(WriteOnly
, O_WRONLY
);
990 if_FLAGGED_SET(Create
, O_CREAT
);
991 if_FLAGGED_SET(Empty
, O_TRUNC
);
992 if_FLAGGED_SET(Exclusive
, O_EXCL
);
993 #undef if_FLAGGED_SET
995 if ((Mode
& Atomic
) == Atomic
)
997 char *name
= strdup((FileName
+ ".XXXXXX").c_str());
999 if((iFd
= mkstemp(name
)) == -1)
1002 return FileFdErrno("mkstemp", "Could not create temporary file for %s", FileName
.c_str());
1005 TemporaryFileName
= string(name
);
1008 if(Perms
!= 600 && fchmod(iFd
, Perms
) == -1)
1009 return FileFdErrno("fchmod", "Could not change permissions for temporary file %s", TemporaryFileName
.c_str());
1012 iFd
= open(FileName
.c_str(), fileflags
, Perms
);
1014 this->FileName
= FileName
;
1015 if (iFd
== -1 || OpenInternDescriptor(Mode
, compressor
) == false)
1022 return FileFdErrno("open",_("Could not open file %s"), FileName
.c_str());
1025 SetCloseExec(iFd
,true);
1029 // FileFd::OpenDescriptor - Open a filedescriptor /*{{{*/
1030 // ---------------------------------------------------------------------
1032 bool FileFd::OpenDescriptor(int Fd
, unsigned int const Mode
, CompressMode Compress
, bool AutoClose
)
1034 std::vector
<APT::Configuration::Compressor
> const compressors
= APT::Configuration::getCompressors();
1035 std::vector
<APT::Configuration::Compressor
>::const_iterator compressor
= compressors
.begin();
1038 // compat with the old API
1039 if (Mode
== ReadOnlyGzip
&& Compress
== None
)
1044 case None
: name
= "."; break;
1045 case Gzip
: name
= "gzip"; break;
1046 case Bzip2
: name
= "bzip2"; break;
1047 case Lzma
: name
= "lzma"; break;
1048 case Xz
: name
= "xz"; break;
1051 if (AutoClose
== true && Fd
!= -1)
1053 return FileFdError("Opening Fd %d in Auto or Extension compression mode is not supported", Fd
);
1055 for (; compressor
!= compressors
.end(); ++compressor
)
1056 if (compressor
->Name
== name
)
1058 if (compressor
== compressors
.end())
1060 if (AutoClose
== true && Fd
!= -1)
1062 return FileFdError("Can't find a configured compressor %s for file %s", name
.c_str(), FileName
.c_str());
1064 return OpenDescriptor(Fd
, Mode
, *compressor
, AutoClose
);
1066 bool FileFd::OpenDescriptor(int Fd
, unsigned int const Mode
, APT::Configuration::Compressor
const &compressor
, bool AutoClose
)
1069 Flags
= (AutoClose
) ? FileFd::AutoClose
: 0;
1070 if (AutoClose
== false && (
1072 compressor
.Name
== "gzip" ||
1075 compressor
.Name
== "bzip2" ||
1079 // Need to duplicate fd here or gzclose for cleanup will close the fd as well
1084 this->FileName
= "";
1085 if (Fd
== -1 || OpenInternDescriptor(Mode
, compressor
) == false)
1089 compressor
.Name
== "gzip" ||
1092 compressor
.Name
== "bzip2" ||
1099 return FileFdError(_("Could not open file descriptor %d"), Fd
);
1103 bool FileFd::OpenInternDescriptor(unsigned int const Mode
, APT::Configuration::Compressor
const &compressor
)
1105 if (compressor
.Name
== "." || compressor
.Binary
.empty() == true)
1110 d
= new FileFdPrivate();
1112 d
->compressor
= compressor
;
1116 if (compressor
.Name
== "gzip")
1123 if ((Mode
& ReadWrite
) == ReadWrite
)
1124 d
->gz
= gzdopen(iFd
, "r+");
1125 else if ((Mode
& WriteOnly
) == WriteOnly
)
1126 d
->gz
= gzdopen(iFd
, "w");
1128 d
->gz
= gzdopen(iFd
, "r");
1131 Flags
|= Compressed
;
1136 if (compressor
.Name
== "bzip2")
1140 BZ2_bzclose(d
->bz2
);
1143 if ((Mode
& ReadWrite
) == ReadWrite
)
1144 d
->bz2
= BZ2_bzdopen(iFd
, "r+");
1145 else if ((Mode
& WriteOnly
) == WriteOnly
)
1146 d
->bz2
= BZ2_bzdopen(iFd
, "w");
1148 d
->bz2
= BZ2_bzdopen(iFd
, "r");
1151 Flags
|= Compressed
;
1156 // collect zombies here in case we reopen
1157 if (d
->compressor_pid
> 0)
1158 ExecWait(d
->compressor_pid
, "FileFdCompressor", true);
1160 if ((Mode
& ReadWrite
) == ReadWrite
)
1161 return FileFdError("ReadWrite mode is not supported for file %s", FileName
.c_str());
1163 bool const Comp
= (Mode
& WriteOnly
) == WriteOnly
;
1166 // Handle 'decompression' of empty files
1169 if (Buf
.st_size
== 0 && S_ISFIFO(Buf
.st_mode
) == false)
1172 // We don't need the file open - instead let the compressor open it
1173 // as he properly knows better how to efficiently read from 'his' file
1174 if (FileName
.empty() == false)
1181 // Create a data pipe
1182 int Pipe
[2] = {-1,-1};
1183 if (pipe(Pipe
) != 0)
1184 return FileFdErrno("pipe",_("Failed to create subprocess IPC"));
1185 for (int J
= 0; J
!= 2; J
++)
1186 SetCloseExec(Pipe
[J
],true);
1188 d
->compressed_fd
= iFd
;
1197 d
->compressor_pid
= ExecFork();
1198 if (d
->compressor_pid
== 0)
1202 dup2(d
->compressed_fd
,STDOUT_FILENO
);
1203 dup2(Pipe
[0],STDIN_FILENO
);
1207 if (FileName
.empty() == true)
1208 dup2(d
->compressed_fd
,STDIN_FILENO
);
1209 dup2(Pipe
[1],STDOUT_FILENO
);
1211 int const nullfd
= open("/dev/null", O_WRONLY
);
1214 dup2(nullfd
,STDERR_FILENO
);
1218 SetCloseExec(STDOUT_FILENO
,false);
1219 SetCloseExec(STDIN_FILENO
,false);
1221 std::vector
<char const*> Args
;
1222 Args
.push_back(compressor
.Binary
.c_str());
1223 std::vector
<std::string
> const * const addArgs
=
1224 (Comp
== true) ? &(compressor
.CompressArgs
) : &(compressor
.UncompressArgs
);
1225 for (std::vector
<std::string
>::const_iterator a
= addArgs
->begin();
1226 a
!= addArgs
->end(); ++a
)
1227 Args
.push_back(a
->c_str());
1228 if (Comp
== false && FileName
.empty() == false)
1230 Args
.push_back("--stdout");
1231 if (TemporaryFileName
.empty() == false)
1232 Args
.push_back(TemporaryFileName
.c_str());
1234 Args
.push_back(FileName
.c_str());
1236 Args
.push_back(NULL
);
1238 execvp(Args
[0],(char **)&Args
[0]);
1239 cerr
<< _("Failed to exec compressor ") << Args
[0] << endl
;
1250 // FileFd::~File - Closes the file /*{{{*/
1251 // ---------------------------------------------------------------------
1252 /* If the proper modes are selected then we close the Fd and possibly
1253 unlink the file on error. */
1258 d
->CloseDown(FileName
);
1263 // FileFd::Read - Read a bit of the file /*{{{*/
1264 // ---------------------------------------------------------------------
1265 /* We are careful to handle interruption by a signal while reading
1267 bool FileFd::Read(void *To
,unsigned long long Size
,unsigned long long *Actual
)
1273 *((char *)To
) = '\0';
1277 if (d
!= NULL
&& d
->gz
!= NULL
)
1278 Res
= gzread(d
->gz
,To
,Size
);
1282 if (d
!= NULL
&& d
->bz2
!= NULL
)
1283 Res
= BZ2_bzread(d
->bz2
,To
,Size
);
1286 Res
= read(iFd
,To
,Size
);
1293 if (d
!= NULL
&& d
->gz
!= NULL
)
1296 char const * const errmsg
= gzerror(d
->gz
, &err
);
1298 return FileFdError("gzread: %s (%d: %s)", _("Read error"), err
, errmsg
);
1302 if (d
!= NULL
&& d
->bz2
!= NULL
)
1305 char const * const errmsg
= BZ2_bzerror(d
->bz2
, &err
);
1306 if (err
!= BZ_IO_ERROR
)
1307 return FileFdError("BZ2_bzread: %s (%d: %s)", _("Read error"), err
, errmsg
);
1310 return FileFdErrno("read",_("Read error"));
1313 To
= (char *)To
+ Res
;
1320 while (Res
> 0 && Size
> 0);
1332 return FileFdError(_("read, still have %llu to read but none left"), Size
);
1335 // FileFd::ReadLine - Read a complete line from the file /*{{{*/
1336 // ---------------------------------------------------------------------
1337 /* Beware: This method can be quiet slow for big buffers on UNcompressed
1338 files because of the naive implementation! */
1339 char* FileFd::ReadLine(char *To
, unsigned long long const Size
)
1343 if (d
!= NULL
&& d
->gz
!= NULL
)
1344 return gzgets(d
->gz
, To
, Size
);
1347 unsigned long long read
= 0;
1348 while ((Size
- 1) != read
)
1350 unsigned long long done
= 0;
1351 if (Read(To
+ read
, 1, &done
) == false)
1355 if (To
[read
++] == '\n')
1364 // FileFd::Write - Write to the file /*{{{*/
1365 // ---------------------------------------------------------------------
1367 bool FileFd::Write(const void *From
,unsigned long long Size
)
1374 if (d
!= NULL
&& d
->gz
!= NULL
)
1375 Res
= gzwrite(d
->gz
,From
,Size
);
1379 if (d
!= NULL
&& d
->bz2
!= NULL
)
1380 Res
= BZ2_bzwrite(d
->bz2
,(void*)From
,Size
);
1383 Res
= write(iFd
,From
,Size
);
1384 if (Res
< 0 && errno
== EINTR
)
1389 if (d
!= NULL
&& d
->gz
!= NULL
)
1392 char const * const errmsg
= gzerror(d
->gz
, &err
);
1394 return FileFdError("gzwrite: %s (%d: %s)", _("Write error"), err
, errmsg
);
1398 if (d
!= NULL
&& d
->bz2
!= NULL
)
1401 char const * const errmsg
= BZ2_bzerror(d
->bz2
, &err
);
1402 if (err
!= BZ_IO_ERROR
)
1403 return FileFdError("BZ2_bzwrite: %s (%d: %s)", _("Write error"), err
, errmsg
);
1406 return FileFdErrno("write",_("Write error"));
1409 From
= (char const *)From
+ Res
;
1414 while (Res
> 0 && Size
> 0);
1419 return FileFdError(_("write, still have %llu to write but couldn't"), Size
);
1421 bool FileFd::Write(int Fd
, const void *From
, unsigned long long Size
)
1427 Res
= write(Fd
,From
,Size
);
1428 if (Res
< 0 && errno
== EINTR
)
1431 return _error
->Errno("write",_("Write error"));
1433 From
= (char const *)From
+ Res
;
1436 while (Res
> 0 && Size
> 0);
1441 return _error
->Error(_("write, still have %llu to write but couldn't"), Size
);
1444 // FileFd::Seek - Seek in the file /*{{{*/
1445 // ---------------------------------------------------------------------
1447 bool FileFd::Seek(unsigned long long To
)
1449 if (d
!= NULL
&& (d
->pipe
== true
1455 // Our poor man seeking in pipes is costly, so try to avoid it
1456 unsigned long long seekpos
= Tell();
1459 else if (seekpos
< To
)
1460 return Skip(To
- seekpos
);
1462 if ((d
->openmode
& ReadOnly
) != ReadOnly
)
1463 return FileFdError("Reopen is only implemented for read-only files!");
1467 BZ2_bzclose(d
->bz2
);
1474 if (TemporaryFileName
.empty() == false)
1475 iFd
= open(TemporaryFileName
.c_str(), O_RDONLY
);
1476 else if (FileName
.empty() == false)
1477 iFd
= open(FileName
.c_str(), O_RDONLY
);
1480 if (d
->compressed_fd
> 0)
1481 if (lseek(d
->compressed_fd
, 0, SEEK_SET
) != 0)
1482 iFd
= d
->compressed_fd
;
1484 return FileFdError("Reopen is not implemented for pipes opened with FileFd::OpenDescriptor()!");
1487 if (OpenInternDescriptor(d
->openmode
, d
->compressor
) == false)
1488 return FileFdError("Seek on file %s because it couldn't be reopened", FileName
.c_str());
1498 if (d
!= NULL
&& d
->gz
)
1499 res
= gzseek(d
->gz
,To
,SEEK_SET
);
1502 res
= lseek(iFd
,To
,SEEK_SET
);
1503 if (res
!= (off_t
)To
)
1504 return FileFdError("Unable to seek to %llu", To
);
1511 // FileFd::Skip - Seek in the file /*{{{*/
1512 // ---------------------------------------------------------------------
1514 bool FileFd::Skip(unsigned long long Over
)
1516 if (d
!= NULL
&& (d
->pipe
== true
1526 unsigned long long toread
= std::min((unsigned long long) sizeof(buffer
), Over
);
1527 if (Read(buffer
, toread
) == false)
1528 return FileFdError("Unable to seek ahead %llu",Over
);
1536 if (d
!= NULL
&& d
->gz
!= NULL
)
1537 res
= gzseek(d
->gz
,Over
,SEEK_CUR
);
1540 res
= lseek(iFd
,Over
,SEEK_CUR
);
1542 return FileFdError("Unable to seek ahead %llu",Over
);
1549 // FileFd::Truncate - Truncate the file /*{{{*/
1550 // ---------------------------------------------------------------------
1552 bool FileFd::Truncate(unsigned long long To
)
1554 // truncating /dev/null is always successful - as we get an error otherwise
1555 if (To
== 0 && FileName
== "/dev/null")
1557 #if defined HAVE_ZLIB || defined HAVE_BZ2
1558 if (d
!= NULL
&& (d
->gz
!= NULL
|| d
->bz2
!= NULL
))
1559 return FileFdError("Truncating compressed files is not implemented (%s)", FileName
.c_str());
1561 if (ftruncate(iFd
,To
) != 0)
1562 return FileFdError("Unable to truncate to %llu",To
);
1567 // FileFd::Tell - Current seek position /*{{{*/
1568 // ---------------------------------------------------------------------
1570 unsigned long long FileFd::Tell()
1572 // In theory, we could just return seekpos here always instead of
1573 // seeking around, but not all users of FileFd use always Seek() and co
1574 // so d->seekpos isn't always true and we can just use it as a hint if
1575 // we have nothing else, but not always as an authority…
1576 if (d
!= NULL
&& (d
->pipe
== true
1585 if (d
!= NULL
&& d
->gz
!= NULL
)
1586 Res
= gztell(d
->gz
);
1589 Res
= lseek(iFd
,0,SEEK_CUR
);
1590 if (Res
== (off_t
)-1)
1591 FileFdErrno("lseek","Failed to determine the current file position");
1597 static bool StatFileFd(char const * const msg
, int const iFd
, std::string
const &FileName
, struct stat
&Buf
, FileFdPrivate
* const d
) /*{{{*/
1599 bool ispipe
= (d
!= NULL
&& d
->pipe
== true);
1600 if (ispipe
== false)
1602 if (fstat(iFd
,&Buf
) != 0)
1603 // higher-level code will generate more meaningful messages,
1604 // even translated this would be meaningless for users
1605 return _error
->Errno("fstat", "Unable to determine %s for fd %i", msg
, iFd
);
1606 ispipe
= S_ISFIFO(Buf
.st_mode
);
1609 // for compressor pipes st_size is undefined and at 'best' zero
1612 // we set it here, too, as we get the info here for free
1613 // in theory the Open-methods should take care of it already
1616 if (stat(FileName
.c_str(), &Buf
) != 0)
1617 return _error
->Errno("fstat", "Unable to determine %s for file %s", msg
, FileName
.c_str());
1622 // FileFd::FileSize - Return the size of the file /*{{{*/
1623 unsigned long long FileFd::FileSize()
1626 if (StatFileFd("file size", iFd
, FileName
, Buf
, d
) == false)
1634 // FileFd::ModificationTime - Return the time of last touch /*{{{*/
1635 time_t FileFd::ModificationTime()
1638 if (StatFileFd("modification time", iFd
, FileName
, Buf
, d
) == false)
1643 return Buf
.st_mtime
;
1646 // FileFd::Size - Return the size of the content in the file /*{{{*/
1647 // ---------------------------------------------------------------------
1649 unsigned long long FileFd::Size()
1651 unsigned long long size
= FileSize();
1653 // for compressor pipes st_size is undefined and at 'best' zero,
1654 // so we 'read' the content and 'seek' back - see there
1655 if (d
!= NULL
&& (d
->pipe
== true
1657 || (d
->bz2
&& size
> 0)
1661 unsigned long long const oldSeek
= Tell();
1663 unsigned long long read
= 0;
1665 if (Read(ignore
, sizeof(ignore
), &read
) == false)
1675 // only check gzsize if we are actually a gzip file, just checking for
1676 // "gz" is not sufficient as uncompressed files could be opened with
1677 // gzopen in "direct" mode as well
1678 else if (d
!= NULL
&& d
->gz
&& !gzdirect(d
->gz
) && size
> 0)
1680 off_t
const oldPos
= lseek(iFd
,0,SEEK_CUR
);
1681 /* unfortunately zlib.h doesn't provide a gzsize(), so we have to do
1682 * this ourselves; the original (uncompressed) file size is the last 32
1683 * bits of the file */
1684 // FIXME: Size for gz-files is limited by 32bit… no largefile support
1685 if (lseek(iFd
, -4, SEEK_END
) < 0)
1687 FileFdErrno("lseek","Unable to seek to end of gzipped file");
1691 if (read(iFd
, &size
, 4) != 4)
1693 FileFdErrno("read","Unable to read original size of gzipped file");
1697 #ifdef WORDS_BIGENDIAN
1698 uint32_t tmp_size
= size
;
1699 uint8_t const * const p
= (uint8_t const * const) &tmp_size
;
1700 tmp_size
= (p
[3] << 24) | (p
[2] << 16) | (p
[1] << 8) | p
[0];
1704 if (lseek(iFd
, oldPos
, SEEK_SET
) < 0)
1706 FileFdErrno("lseek","Unable to seek in gzipped file");
1717 // FileFd::Close - Close the file if the close flag is set /*{{{*/
1718 // ---------------------------------------------------------------------
1720 bool FileFd::Close()
1726 if ((Flags
& AutoClose
) == AutoClose
)
1728 if ((Flags
& Compressed
) != Compressed
&& iFd
> 0 && close(iFd
) != 0)
1729 Res
&= _error
->Errno("close",_("Problem closing the file %s"), FileName
.c_str());
1733 Res
&= d
->CloseDown(FileName
);
1739 if ((Flags
& Replace
) == Replace
) {
1740 if (rename(TemporaryFileName
.c_str(), FileName
.c_str()) != 0)
1741 Res
&= _error
->Errno("rename",_("Problem renaming the file %s to %s"), TemporaryFileName
.c_str(), FileName
.c_str());
1743 FileName
= TemporaryFileName
; // for the unlink() below.
1744 TemporaryFileName
.clear();
1749 if ((Flags
& Fail
) == Fail
&& (Flags
& DelOnFail
) == DelOnFail
&&
1750 FileName
.empty() == false)
1751 if (unlink(FileName
.c_str()) != 0)
1752 Res
&= _error
->WarningE("unlnk",_("Problem unlinking the file %s"), FileName
.c_str());
1759 // FileFd::Sync - Sync the file /*{{{*/
1760 // ---------------------------------------------------------------------
1764 if (fsync(iFd
) != 0)
1765 return FileFdErrno("sync",_("Problem syncing the file"));
1769 // FileFd::FileFdErrno - set Fail and call _error->Errno *{{{*/
1770 bool FileFd::FileFdErrno(const char *Function
, const char *Description
,...)
1774 size_t msgSize
= 400;
1775 int const errsv
= errno
;
1778 va_start(args
,Description
);
1779 if (_error
->InsertErrno(GlobalError::ERROR
, Function
, Description
, args
, errsv
, msgSize
) == false)
1786 // FileFd::FileFdError - set Fail and call _error->Error *{{{*/
1787 bool FileFd::FileFdError(const char *Description
,...) {
1790 size_t msgSize
= 400;
1793 va_start(args
,Description
);
1794 if (_error
->Insert(GlobalError::ERROR
, Description
, args
, msgSize
) == false)
1802 gzFile
FileFd::gzFd() { return d
->gz
; }
1805 // Glob - wrapper around "glob()" /*{{{*/
1806 // ---------------------------------------------------------------------
1808 std::vector
<std::string
> Glob(std::string
const &pattern
, int flags
)
1810 std::vector
<std::string
> result
;
1815 glob_res
= glob(pattern
.c_str(), flags
, NULL
, &globbuf
);
1819 if(glob_res
!= GLOB_NOMATCH
) {
1820 _error
->Errno("glob", "Problem with glob");
1826 for(i
=0;i
<globbuf
.gl_pathc
;i
++)
1827 result
.push_back(string(globbuf
.gl_pathv
[i
]));
1834 std::string
GetTempDir()
1836 const char *tmpdir
= getenv("TMPDIR");
1843 // check that tmpdir is set and exists
1845 if (!tmpdir
|| strlen(tmpdir
) == 0 || stat(tmpdir
, &st
) != 0)
1848 return string(tmpdir
);