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;
1071 this->FileName
= "";
1072 if (OpenInternDescriptor(Mode
, compressor
) == false)
1075 (Flags
& Compressed
) == Compressed
||
1081 return FileFdError(_("Could not open file descriptor %d"), Fd
);
1085 bool FileFd::OpenInternDescriptor(unsigned int const Mode
, APT::Configuration::Compressor
const &compressor
)
1089 if (compressor
.Name
== "." || compressor
.Binary
.empty() == true)
1092 #if defined HAVE_ZLIB || defined HAVE_BZ2
1093 // the API to open files is similar, so setup to avoid code duplicates later
1094 // and while at it ensure that we close before opening (if its a reopen)
1095 void* (*compress_open
)(int, const char *) = NULL
;
1096 #define APT_COMPRESS_INIT(NAME,OPEN,CLOSE,STRUCT) \
1097 if (compressor.Name == NAME) \
1099 compress_open = (void*(*)(int, const char *)) OPEN; \
1100 if (d != NULL && STRUCT != NULL) { CLOSE(STRUCT); STRUCT = NULL; } \
1103 APT_COMPRESS_INIT("gzip", gzdopen
, gzclose
, d
->gz
)
1106 APT_COMPRESS_INIT("bzip2", BZ2_bzdopen
, BZ2_bzclose
, d
->bz2
)
1108 #undef APT_COMPRESS_INIT
1113 d
= new FileFdPrivate();
1115 d
->compressor
= compressor
;
1116 #if defined HAVE_ZLIB || defined HAVE_BZ2
1117 if (AutoClose
== false && compress_open
!= NULL
)
1119 // Need to duplicate fd here or gz/bz2 close for cleanup will close the fd as well
1120 int const internFd
= dup(iFd
);
1122 return FileFdErrno("OpenInternDescriptor", _("Could not open file descriptor %d"), iFd
);
1128 #if defined HAVE_ZLIB || defined HAVE_BZ2
1129 if (compress_open
!= NULL
)
1131 void* compress_struct
= NULL
;
1132 if ((Mode
& ReadWrite
) == ReadWrite
)
1133 compress_struct
= compress_open(iFd
, "r+");
1134 else if ((Mode
& WriteOnly
) == WriteOnly
)
1135 compress_struct
= compress_open(iFd
, "w");
1137 compress_struct
= compress_open(iFd
, "r");
1138 if (compress_struct
== NULL
)
1142 if (compressor
.Name
== "gzip")
1143 d
->gz
= (gzFile
) compress_struct
;
1146 if (compressor
.Name
== "bzip2")
1147 d
->bz2
= (BZFILE
*) compress_struct
;
1149 Flags
|= Compressed
;
1154 // collect zombies here in case we reopen
1155 if (d
->compressor_pid
> 0)
1156 ExecWait(d
->compressor_pid
, "FileFdCompressor", true);
1158 if ((Mode
& ReadWrite
) == ReadWrite
)
1159 return FileFdError("ReadWrite mode is not supported for file %s", FileName
.c_str());
1161 bool const Comp
= (Mode
& WriteOnly
) == WriteOnly
;
1164 // Handle 'decompression' of empty files
1167 if (Buf
.st_size
== 0 && S_ISFIFO(Buf
.st_mode
) == false)
1170 // We don't need the file open - instead let the compressor open it
1171 // as he properly knows better how to efficiently read from 'his' file
1172 if (FileName
.empty() == false)
1179 // Create a data pipe
1180 int Pipe
[2] = {-1,-1};
1181 if (pipe(Pipe
) != 0)
1182 return FileFdErrno("pipe",_("Failed to create subprocess IPC"));
1183 for (int J
= 0; J
!= 2; J
++)
1184 SetCloseExec(Pipe
[J
],true);
1186 d
->compressed_fd
= iFd
;
1195 d
->compressor_pid
= ExecFork();
1196 if (d
->compressor_pid
== 0)
1200 dup2(d
->compressed_fd
,STDOUT_FILENO
);
1201 dup2(Pipe
[0],STDIN_FILENO
);
1205 if (FileName
.empty() == true)
1206 dup2(d
->compressed_fd
,STDIN_FILENO
);
1207 dup2(Pipe
[1],STDOUT_FILENO
);
1209 int const nullfd
= open("/dev/null", O_WRONLY
);
1212 dup2(nullfd
,STDERR_FILENO
);
1216 SetCloseExec(STDOUT_FILENO
,false);
1217 SetCloseExec(STDIN_FILENO
,false);
1219 std::vector
<char const*> Args
;
1220 Args
.push_back(compressor
.Binary
.c_str());
1221 std::vector
<std::string
> const * const addArgs
=
1222 (Comp
== true) ? &(compressor
.CompressArgs
) : &(compressor
.UncompressArgs
);
1223 for (std::vector
<std::string
>::const_iterator a
= addArgs
->begin();
1224 a
!= addArgs
->end(); ++a
)
1225 Args
.push_back(a
->c_str());
1226 if (Comp
== false && FileName
.empty() == false)
1228 Args
.push_back("--stdout");
1229 if (TemporaryFileName
.empty() == false)
1230 Args
.push_back(TemporaryFileName
.c_str());
1232 Args
.push_back(FileName
.c_str());
1234 Args
.push_back(NULL
);
1236 execvp(Args
[0],(char **)&Args
[0]);
1237 cerr
<< _("Failed to exec compressor ") << Args
[0] << endl
;
1248 // FileFd::~File - Closes the file /*{{{*/
1249 // ---------------------------------------------------------------------
1250 /* If the proper modes are selected then we close the Fd and possibly
1251 unlink the file on error. */
1256 d
->CloseDown(FileName
);
1261 // FileFd::Read - Read a bit of the file /*{{{*/
1262 // ---------------------------------------------------------------------
1263 /* We are careful to handle interruption by a signal while reading
1265 bool FileFd::Read(void *To
,unsigned long long Size
,unsigned long long *Actual
)
1271 *((char *)To
) = '\0';
1275 if (d
!= NULL
&& d
->gz
!= NULL
)
1276 Res
= gzread(d
->gz
,To
,Size
);
1280 if (d
!= NULL
&& d
->bz2
!= NULL
)
1281 Res
= BZ2_bzread(d
->bz2
,To
,Size
);
1284 Res
= read(iFd
,To
,Size
);
1291 if (d
!= NULL
&& d
->gz
!= NULL
)
1294 char const * const errmsg
= gzerror(d
->gz
, &err
);
1296 return FileFdError("gzread: %s (%d: %s)", _("Read error"), err
, errmsg
);
1300 if (d
!= NULL
&& d
->bz2
!= NULL
)
1303 char const * const errmsg
= BZ2_bzerror(d
->bz2
, &err
);
1304 if (err
!= BZ_IO_ERROR
)
1305 return FileFdError("BZ2_bzread: %s (%d: %s)", _("Read error"), err
, errmsg
);
1308 return FileFdErrno("read",_("Read error"));
1311 To
= (char *)To
+ Res
;
1318 while (Res
> 0 && Size
> 0);
1330 return FileFdError(_("read, still have %llu to read but none left"), Size
);
1333 // FileFd::ReadLine - Read a complete line from the file /*{{{*/
1334 // ---------------------------------------------------------------------
1335 /* Beware: This method can be quiet slow for big buffers on UNcompressed
1336 files because of the naive implementation! */
1337 char* FileFd::ReadLine(char *To
, unsigned long long const Size
)
1341 if (d
!= NULL
&& d
->gz
!= NULL
)
1342 return gzgets(d
->gz
, To
, Size
);
1345 unsigned long long read
= 0;
1346 while ((Size
- 1) != read
)
1348 unsigned long long done
= 0;
1349 if (Read(To
+ read
, 1, &done
) == false)
1353 if (To
[read
++] == '\n')
1362 // FileFd::Write - Write to the file /*{{{*/
1363 // ---------------------------------------------------------------------
1365 bool FileFd::Write(const void *From
,unsigned long long Size
)
1372 if (d
!= NULL
&& d
->gz
!= NULL
)
1373 Res
= gzwrite(d
->gz
,From
,Size
);
1377 if (d
!= NULL
&& d
->bz2
!= NULL
)
1378 Res
= BZ2_bzwrite(d
->bz2
,(void*)From
,Size
);
1381 Res
= write(iFd
,From
,Size
);
1382 if (Res
< 0 && errno
== EINTR
)
1387 if (d
!= NULL
&& d
->gz
!= NULL
)
1390 char const * const errmsg
= gzerror(d
->gz
, &err
);
1392 return FileFdError("gzwrite: %s (%d: %s)", _("Write error"), err
, errmsg
);
1396 if (d
!= NULL
&& d
->bz2
!= NULL
)
1399 char const * const errmsg
= BZ2_bzerror(d
->bz2
, &err
);
1400 if (err
!= BZ_IO_ERROR
)
1401 return FileFdError("BZ2_bzwrite: %s (%d: %s)", _("Write error"), err
, errmsg
);
1404 return FileFdErrno("write",_("Write error"));
1407 From
= (char const *)From
+ Res
;
1412 while (Res
> 0 && Size
> 0);
1417 return FileFdError(_("write, still have %llu to write but couldn't"), Size
);
1419 bool FileFd::Write(int Fd
, const void *From
, unsigned long long Size
)
1425 Res
= write(Fd
,From
,Size
);
1426 if (Res
< 0 && errno
== EINTR
)
1429 return _error
->Errno("write",_("Write error"));
1431 From
= (char const *)From
+ Res
;
1434 while (Res
> 0 && Size
> 0);
1439 return _error
->Error(_("write, still have %llu to write but couldn't"), Size
);
1442 // FileFd::Seek - Seek in the file /*{{{*/
1443 // ---------------------------------------------------------------------
1445 bool FileFd::Seek(unsigned long long To
)
1447 if (d
!= NULL
&& (d
->pipe
== true
1453 // Our poor man seeking in pipes is costly, so try to avoid it
1454 unsigned long long seekpos
= Tell();
1457 else if (seekpos
< To
)
1458 return Skip(To
- seekpos
);
1460 if ((d
->openmode
& ReadOnly
) != ReadOnly
)
1461 return FileFdError("Reopen is only implemented for read-only files!");
1465 BZ2_bzclose(d
->bz2
);
1472 if (TemporaryFileName
.empty() == false)
1473 iFd
= open(TemporaryFileName
.c_str(), O_RDONLY
);
1474 else if (FileName
.empty() == false)
1475 iFd
= open(FileName
.c_str(), O_RDONLY
);
1478 if (d
->compressed_fd
> 0)
1479 if (lseek(d
->compressed_fd
, 0, SEEK_SET
) != 0)
1480 iFd
= d
->compressed_fd
;
1482 return FileFdError("Reopen is not implemented for pipes opened with FileFd::OpenDescriptor()!");
1485 if (OpenInternDescriptor(d
->openmode
, d
->compressor
) == false)
1486 return FileFdError("Seek on file %s because it couldn't be reopened", FileName
.c_str());
1496 if (d
!= NULL
&& d
->gz
)
1497 res
= gzseek(d
->gz
,To
,SEEK_SET
);
1500 res
= lseek(iFd
,To
,SEEK_SET
);
1501 if (res
!= (off_t
)To
)
1502 return FileFdError("Unable to seek to %llu", To
);
1509 // FileFd::Skip - Seek in the file /*{{{*/
1510 // ---------------------------------------------------------------------
1512 bool FileFd::Skip(unsigned long long Over
)
1514 if (d
!= NULL
&& (d
->pipe
== true
1524 unsigned long long toread
= std::min((unsigned long long) sizeof(buffer
), Over
);
1525 if (Read(buffer
, toread
) == false)
1526 return FileFdError("Unable to seek ahead %llu",Over
);
1534 if (d
!= NULL
&& d
->gz
!= NULL
)
1535 res
= gzseek(d
->gz
,Over
,SEEK_CUR
);
1538 res
= lseek(iFd
,Over
,SEEK_CUR
);
1540 return FileFdError("Unable to seek ahead %llu",Over
);
1547 // FileFd::Truncate - Truncate the file /*{{{*/
1548 // ---------------------------------------------------------------------
1550 bool FileFd::Truncate(unsigned long long To
)
1552 // truncating /dev/null is always successful - as we get an error otherwise
1553 if (To
== 0 && FileName
== "/dev/null")
1555 #if defined HAVE_ZLIB || defined HAVE_BZ2
1556 if (d
!= NULL
&& (d
->gz
!= NULL
|| d
->bz2
!= NULL
))
1557 return FileFdError("Truncating compressed files is not implemented (%s)", FileName
.c_str());
1559 if (ftruncate(iFd
,To
) != 0)
1560 return FileFdError("Unable to truncate to %llu",To
);
1565 // FileFd::Tell - Current seek position /*{{{*/
1566 // ---------------------------------------------------------------------
1568 unsigned long long FileFd::Tell()
1570 // In theory, we could just return seekpos here always instead of
1571 // seeking around, but not all users of FileFd use always Seek() and co
1572 // so d->seekpos isn't always true and we can just use it as a hint if
1573 // we have nothing else, but not always as an authority…
1574 if (d
!= NULL
&& (d
->pipe
== true
1583 if (d
!= NULL
&& d
->gz
!= NULL
)
1584 Res
= gztell(d
->gz
);
1587 Res
= lseek(iFd
,0,SEEK_CUR
);
1588 if (Res
== (off_t
)-1)
1589 FileFdErrno("lseek","Failed to determine the current file position");
1595 static bool StatFileFd(char const * const msg
, int const iFd
, std::string
const &FileName
, struct stat
&Buf
, FileFdPrivate
* const d
) /*{{{*/
1597 bool ispipe
= (d
!= NULL
&& d
->pipe
== true);
1598 if (ispipe
== false)
1600 if (fstat(iFd
,&Buf
) != 0)
1601 // higher-level code will generate more meaningful messages,
1602 // even translated this would be meaningless for users
1603 return _error
->Errno("fstat", "Unable to determine %s for fd %i", msg
, iFd
);
1604 ispipe
= S_ISFIFO(Buf
.st_mode
);
1607 // for compressor pipes st_size is undefined and at 'best' zero
1610 // we set it here, too, as we get the info here for free
1611 // in theory the Open-methods should take care of it already
1614 if (stat(FileName
.c_str(), &Buf
) != 0)
1615 return _error
->Errno("fstat", "Unable to determine %s for file %s", msg
, FileName
.c_str());
1620 // FileFd::FileSize - Return the size of the file /*{{{*/
1621 unsigned long long FileFd::FileSize()
1624 if (StatFileFd("file size", iFd
, FileName
, Buf
, d
) == false)
1632 // FileFd::ModificationTime - Return the time of last touch /*{{{*/
1633 time_t FileFd::ModificationTime()
1636 if (StatFileFd("modification time", iFd
, FileName
, Buf
, d
) == false)
1641 return Buf
.st_mtime
;
1644 // FileFd::Size - Return the size of the content in the file /*{{{*/
1645 // ---------------------------------------------------------------------
1647 unsigned long long FileFd::Size()
1649 unsigned long long size
= FileSize();
1651 // for compressor pipes st_size is undefined and at 'best' zero,
1652 // so we 'read' the content and 'seek' back - see there
1653 if (d
!= NULL
&& (d
->pipe
== true
1655 || (d
->bz2
&& size
> 0)
1659 unsigned long long const oldSeek
= Tell();
1661 unsigned long long read
= 0;
1663 if (Read(ignore
, sizeof(ignore
), &read
) == false)
1673 // only check gzsize if we are actually a gzip file, just checking for
1674 // "gz" is not sufficient as uncompressed files could be opened with
1675 // gzopen in "direct" mode as well
1676 else if (d
!= NULL
&& d
->gz
&& !gzdirect(d
->gz
) && size
> 0)
1678 off_t
const oldPos
= lseek(iFd
,0,SEEK_CUR
);
1679 /* unfortunately zlib.h doesn't provide a gzsize(), so we have to do
1680 * this ourselves; the original (uncompressed) file size is the last 32
1681 * bits of the file */
1682 // FIXME: Size for gz-files is limited by 32bit… no largefile support
1683 if (lseek(iFd
, -4, SEEK_END
) < 0)
1685 FileFdErrno("lseek","Unable to seek to end of gzipped file");
1689 if (read(iFd
, &size
, 4) != 4)
1691 FileFdErrno("read","Unable to read original size of gzipped file");
1695 #ifdef WORDS_BIGENDIAN
1696 uint32_t tmp_size
= size
;
1697 uint8_t const * const p
= (uint8_t const * const) &tmp_size
;
1698 tmp_size
= (p
[3] << 24) | (p
[2] << 16) | (p
[1] << 8) | p
[0];
1702 if (lseek(iFd
, oldPos
, SEEK_SET
) < 0)
1704 FileFdErrno("lseek","Unable to seek in gzipped file");
1715 // FileFd::Close - Close the file if the close flag is set /*{{{*/
1716 // ---------------------------------------------------------------------
1718 bool FileFd::Close()
1724 if ((Flags
& AutoClose
) == AutoClose
)
1726 if ((Flags
& Compressed
) != Compressed
&& iFd
> 0 && close(iFd
) != 0)
1727 Res
&= _error
->Errno("close",_("Problem closing the file %s"), FileName
.c_str());
1731 Res
&= d
->CloseDown(FileName
);
1737 if ((Flags
& Replace
) == Replace
) {
1738 if (rename(TemporaryFileName
.c_str(), FileName
.c_str()) != 0)
1739 Res
&= _error
->Errno("rename",_("Problem renaming the file %s to %s"), TemporaryFileName
.c_str(), FileName
.c_str());
1741 FileName
= TemporaryFileName
; // for the unlink() below.
1742 TemporaryFileName
.clear();
1747 if ((Flags
& Fail
) == Fail
&& (Flags
& DelOnFail
) == DelOnFail
&&
1748 FileName
.empty() == false)
1749 if (unlink(FileName
.c_str()) != 0)
1750 Res
&= _error
->WarningE("unlnk",_("Problem unlinking the file %s"), FileName
.c_str());
1757 // FileFd::Sync - Sync the file /*{{{*/
1758 // ---------------------------------------------------------------------
1762 if (fsync(iFd
) != 0)
1763 return FileFdErrno("sync",_("Problem syncing the file"));
1767 // FileFd::FileFdErrno - set Fail and call _error->Errno *{{{*/
1768 bool FileFd::FileFdErrno(const char *Function
, const char *Description
,...)
1772 size_t msgSize
= 400;
1773 int const errsv
= errno
;
1776 va_start(args
,Description
);
1777 if (_error
->InsertErrno(GlobalError::ERROR
, Function
, Description
, args
, errsv
, msgSize
) == false)
1784 // FileFd::FileFdError - set Fail and call _error->Error *{{{*/
1785 bool FileFd::FileFdError(const char *Description
,...) {
1788 size_t msgSize
= 400;
1791 va_start(args
,Description
);
1792 if (_error
->Insert(GlobalError::ERROR
, Description
, args
, msgSize
) == false)
1800 gzFile
FileFd::gzFd() { return d
->gz
; }
1803 // Glob - wrapper around "glob()" /*{{{*/
1804 // ---------------------------------------------------------------------
1806 std::vector
<std::string
> Glob(std::string
const &pattern
, int flags
)
1808 std::vector
<std::string
> result
;
1813 glob_res
= glob(pattern
.c_str(), flags
, NULL
, &globbuf
);
1817 if(glob_res
!= GLOB_NOMATCH
) {
1818 _error
->Errno("glob", "Problem with glob");
1824 for(i
=0;i
<globbuf
.gl_pathc
;i
++)
1825 result
.push_back(string(globbuf
.gl_pathv
[i
]));
1832 std::string
GetTempDir()
1834 const char *tmpdir
= getenv("TMPDIR");
1841 // check that tmpdir is set and exists
1843 if (!tmpdir
|| strlen(tmpdir
) == 0 || stat(tmpdir
, &st
) != 0)
1846 return string(tmpdir
);