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)
1094 d
= new FileFdPrivate();
1096 d
->compressor
= compressor
;
1097 if (AutoClose
== false && (
1099 compressor
.Name
== "gzip" ||
1102 compressor
.Name
== "bzip2" ||
1106 // Need to duplicate fd here or gz/bz2 close for cleanup will close the fd as well
1107 int const internFd
= dup(iFd
);
1109 return FileFdErrno("OpenInternDescriptor", _("Could not open file descriptor %d"), iFd
);
1115 if (compressor
.Name
== "gzip")
1122 if ((Mode
& ReadWrite
) == ReadWrite
)
1123 d
->gz
= gzdopen(iFd
, "r+");
1124 else if ((Mode
& WriteOnly
) == WriteOnly
)
1125 d
->gz
= gzdopen(iFd
, "w");
1127 d
->gz
= gzdopen(iFd
, "r");
1130 Flags
|= Compressed
;
1135 if (compressor
.Name
== "bzip2")
1139 BZ2_bzclose(d
->bz2
);
1142 if ((Mode
& ReadWrite
) == ReadWrite
)
1143 d
->bz2
= BZ2_bzdopen(iFd
, "r+");
1144 else if ((Mode
& WriteOnly
) == WriteOnly
)
1145 d
->bz2
= BZ2_bzdopen(iFd
, "w");
1147 d
->bz2
= BZ2_bzdopen(iFd
, "r");
1150 Flags
|= Compressed
;
1155 // collect zombies here in case we reopen
1156 if (d
->compressor_pid
> 0)
1157 ExecWait(d
->compressor_pid
, "FileFdCompressor", true);
1159 if ((Mode
& ReadWrite
) == ReadWrite
)
1160 return FileFdError("ReadWrite mode is not supported for file %s", FileName
.c_str());
1162 bool const Comp
= (Mode
& WriteOnly
) == WriteOnly
;
1165 // Handle 'decompression' of empty files
1168 if (Buf
.st_size
== 0 && S_ISFIFO(Buf
.st_mode
) == false)
1171 // We don't need the file open - instead let the compressor open it
1172 // as he properly knows better how to efficiently read from 'his' file
1173 if (FileName
.empty() == false)
1180 // Create a data pipe
1181 int Pipe
[2] = {-1,-1};
1182 if (pipe(Pipe
) != 0)
1183 return FileFdErrno("pipe",_("Failed to create subprocess IPC"));
1184 for (int J
= 0; J
!= 2; J
++)
1185 SetCloseExec(Pipe
[J
],true);
1187 d
->compressed_fd
= iFd
;
1196 d
->compressor_pid
= ExecFork();
1197 if (d
->compressor_pid
== 0)
1201 dup2(d
->compressed_fd
,STDOUT_FILENO
);
1202 dup2(Pipe
[0],STDIN_FILENO
);
1206 if (FileName
.empty() == true)
1207 dup2(d
->compressed_fd
,STDIN_FILENO
);
1208 dup2(Pipe
[1],STDOUT_FILENO
);
1210 int const nullfd
= open("/dev/null", O_WRONLY
);
1213 dup2(nullfd
,STDERR_FILENO
);
1217 SetCloseExec(STDOUT_FILENO
,false);
1218 SetCloseExec(STDIN_FILENO
,false);
1220 std::vector
<char const*> Args
;
1221 Args
.push_back(compressor
.Binary
.c_str());
1222 std::vector
<std::string
> const * const addArgs
=
1223 (Comp
== true) ? &(compressor
.CompressArgs
) : &(compressor
.UncompressArgs
);
1224 for (std::vector
<std::string
>::const_iterator a
= addArgs
->begin();
1225 a
!= addArgs
->end(); ++a
)
1226 Args
.push_back(a
->c_str());
1227 if (Comp
== false && FileName
.empty() == false)
1229 Args
.push_back("--stdout");
1230 if (TemporaryFileName
.empty() == false)
1231 Args
.push_back(TemporaryFileName
.c_str());
1233 Args
.push_back(FileName
.c_str());
1235 Args
.push_back(NULL
);
1237 execvp(Args
[0],(char **)&Args
[0]);
1238 cerr
<< _("Failed to exec compressor ") << Args
[0] << endl
;
1249 // FileFd::~File - Closes the file /*{{{*/
1250 // ---------------------------------------------------------------------
1251 /* If the proper modes are selected then we close the Fd and possibly
1252 unlink the file on error. */
1257 d
->CloseDown(FileName
);
1262 // FileFd::Read - Read a bit of the file /*{{{*/
1263 // ---------------------------------------------------------------------
1264 /* We are careful to handle interruption by a signal while reading
1266 bool FileFd::Read(void *To
,unsigned long long Size
,unsigned long long *Actual
)
1272 *((char *)To
) = '\0';
1276 if (d
!= NULL
&& d
->gz
!= NULL
)
1277 Res
= gzread(d
->gz
,To
,Size
);
1281 if (d
!= NULL
&& d
->bz2
!= NULL
)
1282 Res
= BZ2_bzread(d
->bz2
,To
,Size
);
1285 Res
= read(iFd
,To
,Size
);
1292 if (d
!= NULL
&& d
->gz
!= NULL
)
1295 char const * const errmsg
= gzerror(d
->gz
, &err
);
1297 return FileFdError("gzread: %s (%d: %s)", _("Read error"), err
, errmsg
);
1301 if (d
!= NULL
&& d
->bz2
!= NULL
)
1304 char const * const errmsg
= BZ2_bzerror(d
->bz2
, &err
);
1305 if (err
!= BZ_IO_ERROR
)
1306 return FileFdError("BZ2_bzread: %s (%d: %s)", _("Read error"), err
, errmsg
);
1309 return FileFdErrno("read",_("Read error"));
1312 To
= (char *)To
+ Res
;
1319 while (Res
> 0 && Size
> 0);
1331 return FileFdError(_("read, still have %llu to read but none left"), Size
);
1334 // FileFd::ReadLine - Read a complete line from the file /*{{{*/
1335 // ---------------------------------------------------------------------
1336 /* Beware: This method can be quiet slow for big buffers on UNcompressed
1337 files because of the naive implementation! */
1338 char* FileFd::ReadLine(char *To
, unsigned long long const Size
)
1342 if (d
!= NULL
&& d
->gz
!= NULL
)
1343 return gzgets(d
->gz
, To
, Size
);
1346 unsigned long long read
= 0;
1347 while ((Size
- 1) != read
)
1349 unsigned long long done
= 0;
1350 if (Read(To
+ read
, 1, &done
) == false)
1354 if (To
[read
++] == '\n')
1363 // FileFd::Write - Write to the file /*{{{*/
1364 // ---------------------------------------------------------------------
1366 bool FileFd::Write(const void *From
,unsigned long long Size
)
1373 if (d
!= NULL
&& d
->gz
!= NULL
)
1374 Res
= gzwrite(d
->gz
,From
,Size
);
1378 if (d
!= NULL
&& d
->bz2
!= NULL
)
1379 Res
= BZ2_bzwrite(d
->bz2
,(void*)From
,Size
);
1382 Res
= write(iFd
,From
,Size
);
1383 if (Res
< 0 && errno
== EINTR
)
1388 if (d
!= NULL
&& d
->gz
!= NULL
)
1391 char const * const errmsg
= gzerror(d
->gz
, &err
);
1393 return FileFdError("gzwrite: %s (%d: %s)", _("Write error"), err
, errmsg
);
1397 if (d
!= NULL
&& d
->bz2
!= NULL
)
1400 char const * const errmsg
= BZ2_bzerror(d
->bz2
, &err
);
1401 if (err
!= BZ_IO_ERROR
)
1402 return FileFdError("BZ2_bzwrite: %s (%d: %s)", _("Write error"), err
, errmsg
);
1405 return FileFdErrno("write",_("Write error"));
1408 From
= (char const *)From
+ Res
;
1413 while (Res
> 0 && Size
> 0);
1418 return FileFdError(_("write, still have %llu to write but couldn't"), Size
);
1420 bool FileFd::Write(int Fd
, const void *From
, unsigned long long Size
)
1426 Res
= write(Fd
,From
,Size
);
1427 if (Res
< 0 && errno
== EINTR
)
1430 return _error
->Errno("write",_("Write error"));
1432 From
= (char const *)From
+ Res
;
1435 while (Res
> 0 && Size
> 0);
1440 return _error
->Error(_("write, still have %llu to write but couldn't"), Size
);
1443 // FileFd::Seek - Seek in the file /*{{{*/
1444 // ---------------------------------------------------------------------
1446 bool FileFd::Seek(unsigned long long To
)
1448 if (d
!= NULL
&& (d
->pipe
== true
1454 // Our poor man seeking in pipes is costly, so try to avoid it
1455 unsigned long long seekpos
= Tell();
1458 else if (seekpos
< To
)
1459 return Skip(To
- seekpos
);
1461 if ((d
->openmode
& ReadOnly
) != ReadOnly
)
1462 return FileFdError("Reopen is only implemented for read-only files!");
1466 BZ2_bzclose(d
->bz2
);
1473 if (TemporaryFileName
.empty() == false)
1474 iFd
= open(TemporaryFileName
.c_str(), O_RDONLY
);
1475 else if (FileName
.empty() == false)
1476 iFd
= open(FileName
.c_str(), O_RDONLY
);
1479 if (d
->compressed_fd
> 0)
1480 if (lseek(d
->compressed_fd
, 0, SEEK_SET
) != 0)
1481 iFd
= d
->compressed_fd
;
1483 return FileFdError("Reopen is not implemented for pipes opened with FileFd::OpenDescriptor()!");
1486 if (OpenInternDescriptor(d
->openmode
, d
->compressor
) == false)
1487 return FileFdError("Seek on file %s because it couldn't be reopened", FileName
.c_str());
1497 if (d
!= NULL
&& d
->gz
)
1498 res
= gzseek(d
->gz
,To
,SEEK_SET
);
1501 res
= lseek(iFd
,To
,SEEK_SET
);
1502 if (res
!= (off_t
)To
)
1503 return FileFdError("Unable to seek to %llu", To
);
1510 // FileFd::Skip - Seek in the file /*{{{*/
1511 // ---------------------------------------------------------------------
1513 bool FileFd::Skip(unsigned long long Over
)
1515 if (d
!= NULL
&& (d
->pipe
== true
1525 unsigned long long toread
= std::min((unsigned long long) sizeof(buffer
), Over
);
1526 if (Read(buffer
, toread
) == false)
1527 return FileFdError("Unable to seek ahead %llu",Over
);
1535 if (d
!= NULL
&& d
->gz
!= NULL
)
1536 res
= gzseek(d
->gz
,Over
,SEEK_CUR
);
1539 res
= lseek(iFd
,Over
,SEEK_CUR
);
1541 return FileFdError("Unable to seek ahead %llu",Over
);
1548 // FileFd::Truncate - Truncate the file /*{{{*/
1549 // ---------------------------------------------------------------------
1551 bool FileFd::Truncate(unsigned long long To
)
1553 // truncating /dev/null is always successful - as we get an error otherwise
1554 if (To
== 0 && FileName
== "/dev/null")
1556 #if defined HAVE_ZLIB || defined HAVE_BZ2
1557 if (d
!= NULL
&& (d
->gz
!= NULL
|| d
->bz2
!= NULL
))
1558 return FileFdError("Truncating compressed files is not implemented (%s)", FileName
.c_str());
1560 if (ftruncate(iFd
,To
) != 0)
1561 return FileFdError("Unable to truncate to %llu",To
);
1566 // FileFd::Tell - Current seek position /*{{{*/
1567 // ---------------------------------------------------------------------
1569 unsigned long long FileFd::Tell()
1571 // In theory, we could just return seekpos here always instead of
1572 // seeking around, but not all users of FileFd use always Seek() and co
1573 // so d->seekpos isn't always true and we can just use it as a hint if
1574 // we have nothing else, but not always as an authority…
1575 if (d
!= NULL
&& (d
->pipe
== true
1584 if (d
!= NULL
&& d
->gz
!= NULL
)
1585 Res
= gztell(d
->gz
);
1588 Res
= lseek(iFd
,0,SEEK_CUR
);
1589 if (Res
== (off_t
)-1)
1590 FileFdErrno("lseek","Failed to determine the current file position");
1596 static bool StatFileFd(char const * const msg
, int const iFd
, std::string
const &FileName
, struct stat
&Buf
, FileFdPrivate
* const d
) /*{{{*/
1598 bool ispipe
= (d
!= NULL
&& d
->pipe
== true);
1599 if (ispipe
== false)
1601 if (fstat(iFd
,&Buf
) != 0)
1602 // higher-level code will generate more meaningful messages,
1603 // even translated this would be meaningless for users
1604 return _error
->Errno("fstat", "Unable to determine %s for fd %i", msg
, iFd
);
1605 ispipe
= S_ISFIFO(Buf
.st_mode
);
1608 // for compressor pipes st_size is undefined and at 'best' zero
1611 // we set it here, too, as we get the info here for free
1612 // in theory the Open-methods should take care of it already
1615 if (stat(FileName
.c_str(), &Buf
) != 0)
1616 return _error
->Errno("fstat", "Unable to determine %s for file %s", msg
, FileName
.c_str());
1621 // FileFd::FileSize - Return the size of the file /*{{{*/
1622 unsigned long long FileFd::FileSize()
1625 if (StatFileFd("file size", iFd
, FileName
, Buf
, d
) == false)
1633 // FileFd::ModificationTime - Return the time of last touch /*{{{*/
1634 time_t FileFd::ModificationTime()
1637 if (StatFileFd("modification time", iFd
, FileName
, Buf
, d
) == false)
1642 return Buf
.st_mtime
;
1645 // FileFd::Size - Return the size of the content in the file /*{{{*/
1646 // ---------------------------------------------------------------------
1648 unsigned long long FileFd::Size()
1650 unsigned long long size
= FileSize();
1652 // for compressor pipes st_size is undefined and at 'best' zero,
1653 // so we 'read' the content and 'seek' back - see there
1654 if (d
!= NULL
&& (d
->pipe
== true
1656 || (d
->bz2
&& size
> 0)
1660 unsigned long long const oldSeek
= Tell();
1662 unsigned long long read
= 0;
1664 if (Read(ignore
, sizeof(ignore
), &read
) == false)
1674 // only check gzsize if we are actually a gzip file, just checking for
1675 // "gz" is not sufficient as uncompressed files could be opened with
1676 // gzopen in "direct" mode as well
1677 else if (d
!= NULL
&& d
->gz
&& !gzdirect(d
->gz
) && size
> 0)
1679 off_t
const oldPos
= lseek(iFd
,0,SEEK_CUR
);
1680 /* unfortunately zlib.h doesn't provide a gzsize(), so we have to do
1681 * this ourselves; the original (uncompressed) file size is the last 32
1682 * bits of the file */
1683 // FIXME: Size for gz-files is limited by 32bit… no largefile support
1684 if (lseek(iFd
, -4, SEEK_END
) < 0)
1686 FileFdErrno("lseek","Unable to seek to end of gzipped file");
1690 if (read(iFd
, &size
, 4) != 4)
1692 FileFdErrno("read","Unable to read original size of gzipped file");
1696 #ifdef WORDS_BIGENDIAN
1697 uint32_t tmp_size
= size
;
1698 uint8_t const * const p
= (uint8_t const * const) &tmp_size
;
1699 tmp_size
= (p
[3] << 24) | (p
[2] << 16) | (p
[1] << 8) | p
[0];
1703 if (lseek(iFd
, oldPos
, SEEK_SET
) < 0)
1705 FileFdErrno("lseek","Unable to seek in gzipped file");
1716 // FileFd::Close - Close the file if the close flag is set /*{{{*/
1717 // ---------------------------------------------------------------------
1719 bool FileFd::Close()
1725 if ((Flags
& AutoClose
) == AutoClose
)
1727 if ((Flags
& Compressed
) != Compressed
&& iFd
> 0 && close(iFd
) != 0)
1728 Res
&= _error
->Errno("close",_("Problem closing the file %s"), FileName
.c_str());
1732 Res
&= d
->CloseDown(FileName
);
1738 if ((Flags
& Replace
) == Replace
) {
1739 if (rename(TemporaryFileName
.c_str(), FileName
.c_str()) != 0)
1740 Res
&= _error
->Errno("rename",_("Problem renaming the file %s to %s"), TemporaryFileName
.c_str(), FileName
.c_str());
1742 FileName
= TemporaryFileName
; // for the unlink() below.
1743 TemporaryFileName
.clear();
1748 if ((Flags
& Fail
) == Fail
&& (Flags
& DelOnFail
) == DelOnFail
&&
1749 FileName
.empty() == false)
1750 if (unlink(FileName
.c_str()) != 0)
1751 Res
&= _error
->WarningE("unlnk",_("Problem unlinking the file %s"), FileName
.c_str());
1758 // FileFd::Sync - Sync the file /*{{{*/
1759 // ---------------------------------------------------------------------
1763 if (fsync(iFd
) != 0)
1764 return FileFdErrno("sync",_("Problem syncing the file"));
1768 // FileFd::FileFdErrno - set Fail and call _error->Errno *{{{*/
1769 bool FileFd::FileFdErrno(const char *Function
, const char *Description
,...)
1773 size_t msgSize
= 400;
1774 int const errsv
= errno
;
1777 va_start(args
,Description
);
1778 if (_error
->InsertErrno(GlobalError::ERROR
, Function
, Description
, args
, errsv
, msgSize
) == false)
1785 // FileFd::FileFdError - set Fail and call _error->Error *{{{*/
1786 bool FileFd::FileFdError(const char *Description
,...) {
1789 size_t msgSize
= 400;
1792 va_start(args
,Description
);
1793 if (_error
->Insert(GlobalError::ERROR
, Description
, args
, msgSize
) == false)
1801 gzFile
FileFd::gzFd() { return d
->gz
; }
1804 // Glob - wrapper around "glob()" /*{{{*/
1805 // ---------------------------------------------------------------------
1807 std::vector
<std::string
> Glob(std::string
const &pattern
, int flags
)
1809 std::vector
<std::string
> result
;
1814 glob_res
= glob(pattern
.c_str(), flags
, NULL
, &globbuf
);
1818 if(glob_res
!= GLOB_NOMATCH
) {
1819 _error
->Errno("glob", "Problem with glob");
1825 for(i
=0;i
<globbuf
.gl_pathc
;i
++)
1826 result
.push_back(string(globbuf
.gl_pathv
[i
]));
1833 std::string
GetTempDir()
1835 const char *tmpdir
= getenv("TMPDIR");
1842 // check that tmpdir is set and exists
1844 if (!tmpdir
|| strlen(tmpdir
) == 0 || stat(tmpdir
, &st
) != 0)
1847 return string(tmpdir
);