1 // -*- mode: cpp; mode: fold -*-
3 // $Id: fileutl.cc,v 1.42 2002/09/14 05:29:22 jgg Exp $
4 /* ######################################################################
8 CopyFile - Buffered copy of a single file
9 GetLock - dpkg compatible lock file manipulation (fcntl)
11 Most of this source is placed in the Public Domain, do with it what
13 It was originally written by Jason Gunthorpe <jgg@debian.org>.
14 FileFd gzip support added by Martin Pitt <martin.pitt@canonical.com>
16 The exception is RunScripts() it is under the GPLv2
18 ##################################################################### */
20 // Include Files /*{{{*/
23 #include <apt-pkg/fileutl.h>
24 #include <apt-pkg/strutl.h>
25 #include <apt-pkg/error.h>
26 #include <apt-pkg/sptr.h>
27 #include <apt-pkg/aptconfiguration.h>
28 #include <apt-pkg/configuration.h>
38 #include <sys/types.h>
54 #ifdef WORDS_BIGENDIAN
78 APT::Configuration::Compressor compressor
;
79 unsigned int openmode
;
80 unsigned long long seekpos
;
81 FileFdPrivate() : gz(NULL
), bz2(NULL
),
82 compressed_fd(-1), compressor_pid(-1), pipe(false),
83 openmode(0), seekpos(0) {};
84 bool CloseDown(std::string
const &FileName
)
89 int const e
= gzclose(gz
);
91 // gzdclose() on empty files always fails with "buffer error" here, ignore that
92 if (e
!= 0 && e
!= Z_BUF_ERROR
)
93 Res
&= _error
->Errno("close",_("Problem closing the gzip file %s"), FileName
.c_str());
102 if (compressor_pid
> 0)
103 ExecWait(compressor_pid
, "FileFdCompressor", true);
108 ~FileFdPrivate() { CloseDown(""); }
111 // RunScripts - Run a set of scripts from a configuration subtree /*{{{*/
112 // ---------------------------------------------------------------------
114 bool RunScripts(const char *Cnf
)
116 Configuration::Item
const *Opts
= _config
->Tree(Cnf
);
117 if (Opts
== 0 || Opts
->Child
== 0)
121 // Fork for running the system calls
122 pid_t Child
= ExecFork();
127 if (_config
->FindDir("DPkg::Chroot-Directory","/") != "/")
129 std::cerr
<< "Chrooting into "
130 << _config
->FindDir("DPkg::Chroot-Directory")
132 if (chroot(_config
->FindDir("DPkg::Chroot-Directory","/").c_str()) != 0)
136 if (chdir("/tmp/") != 0)
139 unsigned int Count
= 1;
140 for (; Opts
!= 0; Opts
= Opts
->Next
, Count
++)
142 if (Opts
->Value
.empty() == true)
145 if (system(Opts
->Value
.c_str()) != 0)
151 // Wait for the child
153 while (waitpid(Child
,&Status
,0) != Child
)
157 return _error
->Errno("waitpid","Couldn't wait for subprocess");
160 // Restore sig int/quit
161 signal(SIGQUIT
,SIG_DFL
);
162 signal(SIGINT
,SIG_DFL
);
164 // Check for an error code.
165 if (WIFEXITED(Status
) == 0 || WEXITSTATUS(Status
) != 0)
167 unsigned int Count
= WEXITSTATUS(Status
);
171 for (; Opts
!= 0 && Count
!= 1; Opts
= Opts
->Next
, Count
--);
172 _error
->Error("Problem executing scripts %s '%s'",Cnf
,Opts
->Value
.c_str());
175 return _error
->Error("Sub-process returned an error code");
182 // CopyFile - Buffered copy of a file /*{{{*/
183 // ---------------------------------------------------------------------
184 /* The caller is expected to set things so that failure causes erasure */
185 bool CopyFile(FileFd
&From
,FileFd
&To
)
187 if (From
.IsOpen() == false || To
.IsOpen() == false ||
188 From
.Failed() == true || To
.Failed() == true)
191 // Buffered copy between fds
192 SPtrArray
<unsigned char> Buf
= new unsigned char[64000];
193 unsigned long long Size
= From
.Size();
196 unsigned long long ToRead
= Size
;
200 if (From
.Read(Buf
,ToRead
) == false ||
201 To
.Write(Buf
,ToRead
) == false)
210 // GetLock - Gets a lock file /*{{{*/
211 // ---------------------------------------------------------------------
212 /* This will create an empty file of the given name and lock it. Once this
213 is done all other calls to GetLock in any other process will fail with
214 -1. The return result is the fd of the file, the call should call
215 close at some time. */
216 int GetLock(string File
,bool Errors
)
218 // GetLock() is used in aptitude on directories with public-write access
219 // Use O_NOFOLLOW here to prevent symlink traversal attacks
220 int FD
= open(File
.c_str(),O_RDWR
| O_CREAT
| O_NOFOLLOW
,0640);
223 // Read only .. cant have locking problems there.
226 _error
->Warning(_("Not using locking for read only lock file %s"),File
.c_str());
227 return dup(0); // Need something for the caller to close
231 _error
->Errno("open",_("Could not open lock file %s"),File
.c_str());
233 // Feh.. We do this to distinguish the lock vs open case..
237 SetCloseExec(FD
,true);
239 // Aquire a write lock
242 fl
.l_whence
= SEEK_SET
;
245 if (fcntl(FD
,F_SETLK
,&fl
) == -1)
249 _error
->Warning(_("Not using locking for nfs mounted lock file %s"),File
.c_str());
250 return dup(0); // Need something for the caller to close
253 _error
->Errno("open",_("Could not get lock %s"),File
.c_str());
264 // FileExists - Check if a file exists /*{{{*/
265 // ---------------------------------------------------------------------
266 /* Beware: Directories are also files! */
267 bool FileExists(string File
)
270 if (stat(File
.c_str(),&Buf
) != 0)
275 // RealFileExists - Check if a file exists and if it is really a file /*{{{*/
276 // ---------------------------------------------------------------------
278 bool RealFileExists(string File
)
281 if (stat(File
.c_str(),&Buf
) != 0)
283 return ((Buf
.st_mode
& S_IFREG
) != 0);
286 // DirectoryExists - Check if a directory exists and is really one /*{{{*/
287 // ---------------------------------------------------------------------
289 bool DirectoryExists(string
const &Path
)
292 if (stat(Path
.c_str(),&Buf
) != 0)
294 return ((Buf
.st_mode
& S_IFDIR
) != 0);
297 // CreateDirectory - poor man's mkdir -p guarded by a parent directory /*{{{*/
298 // ---------------------------------------------------------------------
299 /* This method will create all directories needed for path in good old
300 mkdir -p style but refuses to do this if Parent is not a prefix of
301 this Path. Example: /var/cache/ and /var/cache/apt/archives are given,
302 so it will create apt/archives if /var/cache exists - on the other
303 hand if the parent is /var/lib the creation will fail as this path
304 is not a parent of the path to be generated. */
305 bool CreateDirectory(string
const &Parent
, string
const &Path
)
307 if (Parent
.empty() == true || Path
.empty() == true)
310 if (DirectoryExists(Path
) == true)
313 if (DirectoryExists(Parent
) == false)
316 // we are not going to create directories "into the blue"
317 if (Path
.find(Parent
, 0) != 0)
320 vector
<string
> const dirs
= VectorizeString(Path
.substr(Parent
.size()), '/');
321 string progress
= Parent
;
322 for (vector
<string
>::const_iterator d
= dirs
.begin(); d
!= dirs
.end(); ++d
)
324 if (d
->empty() == true)
327 progress
.append("/").append(*d
);
328 if (DirectoryExists(progress
) == true)
331 if (mkdir(progress
.c_str(), 0755) != 0)
337 // CreateAPTDirectoryIfNeeded - ensure that the given directory exists /*{{{*/
338 // ---------------------------------------------------------------------
339 /* a small wrapper around CreateDirectory to check if it exists and to
340 remove the trailing "/apt/" from the parent directory if needed */
341 bool CreateAPTDirectoryIfNeeded(string
const &Parent
, string
const &Path
)
343 if (DirectoryExists(Path
) == true)
346 size_t const len
= Parent
.size();
347 if (len
> 5 && Parent
.find("/apt/", len
- 6, 5) == len
- 5)
349 if (CreateDirectory(Parent
.substr(0,len
-5), Path
) == true)
352 else if (CreateDirectory(Parent
, Path
) == true)
358 // GetListOfFilesInDir - returns a vector of files in the given dir /*{{{*/
359 // ---------------------------------------------------------------------
360 /* If an extension is given only files with this extension are included
361 in the returned vector, otherwise every "normal" file is included. */
362 std::vector
<string
> GetListOfFilesInDir(string
const &Dir
, string
const &Ext
,
363 bool const &SortList
, bool const &AllowNoExt
)
365 std::vector
<string
> ext
;
367 if (Ext
.empty() == false)
369 if (AllowNoExt
== true && ext
.empty() == false)
371 return GetListOfFilesInDir(Dir
, ext
, SortList
);
373 std::vector
<string
> GetListOfFilesInDir(string
const &Dir
, std::vector
<string
> const &Ext
,
374 bool const &SortList
)
376 // Attention debuggers: need to be set with the environment config file!
377 bool const Debug
= _config
->FindB("Debug::GetListOfFilesInDir", false);
380 std::clog
<< "Accept in " << Dir
<< " only files with the following " << Ext
.size() << " extensions:" << std::endl
;
381 if (Ext
.empty() == true)
382 std::clog
<< "\tNO extension" << std::endl
;
384 for (std::vector
<string
>::const_iterator e
= Ext
.begin();
386 std::clog
<< '\t' << (e
->empty() == true ? "NO" : *e
) << " extension" << std::endl
;
389 std::vector
<string
> List
;
391 if (DirectoryExists(Dir
) == false)
393 _error
->Error(_("List of files can't be created as '%s' is not a directory"), Dir
.c_str());
397 Configuration::MatchAgainstConfig
SilentIgnore("Dir::Ignore-Files-Silently");
398 DIR *D
= opendir(Dir
.c_str());
401 _error
->Errno("opendir",_("Unable to read %s"),Dir
.c_str());
405 for (struct dirent
*Ent
= readdir(D
); Ent
!= 0; Ent
= readdir(D
))
407 // skip "hidden" files
408 if (Ent
->d_name
[0] == '.')
411 // Make sure it is a file and not something else
412 string
const File
= flCombine(Dir
,Ent
->d_name
);
413 #ifdef _DIRENT_HAVE_D_TYPE
414 if (Ent
->d_type
!= DT_REG
)
417 if (RealFileExists(File
) == false)
419 // do not show ignoration warnings for directories
421 #ifdef _DIRENT_HAVE_D_TYPE
422 Ent
->d_type
== DT_DIR
||
424 DirectoryExists(File
) == true)
426 if (SilentIgnore
.Match(Ent
->d_name
) == false)
427 _error
->Notice(_("Ignoring '%s' in directory '%s' as it is not a regular file"), Ent
->d_name
, Dir
.c_str());
432 // check for accepted extension:
433 // no extension given -> periods are bad as hell!
434 // extensions given -> "" extension allows no extension
435 if (Ext
.empty() == false)
437 string d_ext
= flExtension(Ent
->d_name
);
438 if (d_ext
== Ent
->d_name
) // no extension
440 if (std::find(Ext
.begin(), Ext
.end(), "") == Ext
.end())
443 std::clog
<< "Bad file: " << Ent
->d_name
<< " → no extension" << std::endl
;
444 if (SilentIgnore
.Match(Ent
->d_name
) == false)
445 _error
->Notice(_("Ignoring file '%s' in directory '%s' as it has no filename extension"), Ent
->d_name
, Dir
.c_str());
449 else if (std::find(Ext
.begin(), Ext
.end(), d_ext
) == Ext
.end())
452 std::clog
<< "Bad file: " << Ent
->d_name
<< " → bad extension »" << flExtension(Ent
->d_name
) << "«" << std::endl
;
453 if (SilentIgnore
.Match(Ent
->d_name
) == false)
454 _error
->Notice(_("Ignoring file '%s' in directory '%s' as it has an invalid filename extension"), Ent
->d_name
, Dir
.c_str());
459 // Skip bad filenames ala run-parts
460 const char *C
= Ent
->d_name
;
462 if (isalpha(*C
) == 0 && isdigit(*C
) == 0
463 && *C
!= '_' && *C
!= '-') {
464 // no required extension -> dot is a bad character
465 if (*C
== '.' && Ext
.empty() == false)
470 // we don't reach the end of the name -> bad character included
474 std::clog
<< "Bad file: " << Ent
->d_name
<< " → bad character »"
475 << *C
<< "« in filename (period allowed: " << (Ext
.empty() ? "no" : "yes") << ")" << std::endl
;
479 // skip filenames which end with a period. These are never valid
483 std::clog
<< "Bad file: " << Ent
->d_name
<< " → Period as last character" << std::endl
;
488 std::clog
<< "Accept file: " << Ent
->d_name
<< " in " << Dir
<< std::endl
;
489 List
.push_back(File
);
493 if (SortList
== true)
494 std::sort(List
.begin(),List
.end());
497 std::vector
<string
> GetListOfFilesInDir(string
const &Dir
, bool SortList
)
499 bool const Debug
= _config
->FindB("Debug::GetListOfFilesInDir", false);
501 std::clog
<< "Accept in " << Dir
<< " all regular files" << std::endl
;
503 std::vector
<string
> List
;
505 if (DirectoryExists(Dir
) == false)
507 _error
->Error(_("List of files can't be created as '%s' is not a directory"), Dir
.c_str());
511 DIR *D
= opendir(Dir
.c_str());
514 _error
->Errno("opendir",_("Unable to read %s"),Dir
.c_str());
518 for (struct dirent
*Ent
= readdir(D
); Ent
!= 0; Ent
= readdir(D
))
520 // skip "hidden" files
521 if (Ent
->d_name
[0] == '.')
524 // Make sure it is a file and not something else
525 string
const File
= flCombine(Dir
,Ent
->d_name
);
526 #ifdef _DIRENT_HAVE_D_TYPE
527 if (Ent
->d_type
!= DT_REG
)
530 if (RealFileExists(File
) == false)
533 std::clog
<< "Bad file: " << Ent
->d_name
<< " → it is not a real file" << std::endl
;
538 // Skip bad filenames ala run-parts
539 const char *C
= Ent
->d_name
;
541 if (isalpha(*C
) == 0 && isdigit(*C
) == 0
542 && *C
!= '_' && *C
!= '-' && *C
!= '.')
545 // we don't reach the end of the name -> bad character included
549 std::clog
<< "Bad file: " << Ent
->d_name
<< " → bad character »" << *C
<< "« in filename" << std::endl
;
553 // skip filenames which end with a period. These are never valid
557 std::clog
<< "Bad file: " << Ent
->d_name
<< " → Period as last character" << std::endl
;
562 std::clog
<< "Accept file: " << Ent
->d_name
<< " in " << Dir
<< std::endl
;
563 List
.push_back(File
);
567 if (SortList
== true)
568 std::sort(List
.begin(),List
.end());
572 // SafeGetCWD - This is a safer getcwd that returns a dynamic string /*{{{*/
573 // ---------------------------------------------------------------------
574 /* We return / on failure. */
577 // Stash the current dir.
580 if (getcwd(S
,sizeof(S
)-2) == 0)
582 unsigned int Len
= strlen(S
);
588 // GetModificationTime - Get the mtime of the given file or -1 on error /*{{{*/
589 // ---------------------------------------------------------------------
590 /* We return / on failure. */
591 time_t GetModificationTime(string
const &Path
)
594 if (stat(Path
.c_str(), &St
) < 0)
599 // flNotDir - Strip the directory from the filename /*{{{*/
600 // ---------------------------------------------------------------------
602 string
flNotDir(string File
)
604 string::size_type Res
= File
.rfind('/');
605 if (Res
== string::npos
)
608 return string(File
,Res
,Res
- File
.length());
611 // flNotFile - Strip the file from the directory name /*{{{*/
612 // ---------------------------------------------------------------------
613 /* Result ends in a / */
614 string
flNotFile(string File
)
616 string::size_type Res
= File
.rfind('/');
617 if (Res
== string::npos
)
620 return string(File
,0,Res
);
623 // flExtension - Return the extension for the file /*{{{*/
624 // ---------------------------------------------------------------------
626 string
flExtension(string File
)
628 string::size_type Res
= File
.rfind('.');
629 if (Res
== string::npos
)
632 return string(File
,Res
,Res
- File
.length());
635 // flNoLink - If file is a symlink then deref it /*{{{*/
636 // ---------------------------------------------------------------------
637 /* If the name is not a link then the returned path is the input. */
638 string
flNoLink(string File
)
641 if (lstat(File
.c_str(),&St
) != 0 || S_ISLNK(St
.st_mode
) == 0)
643 if (stat(File
.c_str(),&St
) != 0)
646 /* Loop resolving the link. There is no need to limit the number of
647 loops because the stat call above ensures that the symlink is not
655 if ((Res
= readlink(NFile
.c_str(),Buffer
,sizeof(Buffer
))) <= 0 ||
656 (unsigned)Res
>= sizeof(Buffer
))
659 // Append or replace the previous path
661 if (Buffer
[0] == '/')
664 NFile
= flNotFile(NFile
) + Buffer
;
666 // See if we are done
667 if (lstat(NFile
.c_str(),&St
) != 0)
669 if (S_ISLNK(St
.st_mode
) == 0)
674 // flCombine - Combine a file and a directory /*{{{*/
675 // ---------------------------------------------------------------------
676 /* If the file is an absolute path then it is just returned, otherwise
677 the directory is pre-pended to it. */
678 string
flCombine(string Dir
,string File
)
680 if (File
.empty() == true)
683 if (File
[0] == '/' || Dir
.empty() == true)
685 if (File
.length() >= 2 && File
[0] == '.' && File
[1] == '/')
687 if (Dir
[Dir
.length()-1] == '/')
689 return Dir
+ '/' + File
;
692 // SetCloseExec - Set the close on exec flag /*{{{*/
693 // ---------------------------------------------------------------------
695 void SetCloseExec(int Fd
,bool Close
)
697 if (fcntl(Fd
,F_SETFD
,(Close
== false)?0:FD_CLOEXEC
) != 0)
699 cerr
<< "FATAL -> Could not set close on exec " << strerror(errno
) << endl
;
704 // SetNonBlock - Set the nonblocking flag /*{{{*/
705 // ---------------------------------------------------------------------
707 void SetNonBlock(int Fd
,bool Block
)
709 int Flags
= fcntl(Fd
,F_GETFL
) & (~O_NONBLOCK
);
710 if (fcntl(Fd
,F_SETFL
,Flags
| ((Block
== false)?0:O_NONBLOCK
)) != 0)
712 cerr
<< "FATAL -> Could not set non-blocking flag " << strerror(errno
) << endl
;
717 // WaitFd - Wait for a FD to become readable /*{{{*/
718 // ---------------------------------------------------------------------
719 /* This waits for a FD to become readable using select. It is useful for
720 applications making use of non-blocking sockets. The timeout is
722 bool WaitFd(int Fd
,bool write
,unsigned long timeout
)
735 Res
= select(Fd
+1,0,&Set
,0,(timeout
!= 0?&tv
:0));
737 while (Res
< 0 && errno
== EINTR
);
747 Res
= select(Fd
+1,&Set
,0,0,(timeout
!= 0?&tv
:0));
749 while (Res
< 0 && errno
== EINTR
);
758 // ExecFork - Magical fork that sanitizes the context before execing /*{{{*/
759 // ---------------------------------------------------------------------
760 /* This is used if you want to cleanse the environment for the forked
761 child, it fixes up the important signals and nukes all of the fds,
762 otherwise acts like normal fork. */
765 // Fork off the process
766 pid_t Process
= fork();
769 cerr
<< "FATAL -> Failed to fork." << endl
;
773 // Spawn the subprocess
777 signal(SIGPIPE
,SIG_DFL
);
778 signal(SIGQUIT
,SIG_DFL
);
779 signal(SIGINT
,SIG_DFL
);
780 signal(SIGWINCH
,SIG_DFL
);
781 signal(SIGCONT
,SIG_DFL
);
782 signal(SIGTSTP
,SIG_DFL
);
785 Configuration::Item
const *Opts
= _config
->Tree("APT::Keep-Fds");
786 if (Opts
!= 0 && Opts
->Child
!= 0)
789 for (; Opts
!= 0; Opts
= Opts
->Next
)
791 if (Opts
->Value
.empty() == true)
793 int fd
= atoi(Opts
->Value
.c_str());
798 // Close all of our FDs - just in case
799 for (int K
= 3; K
!= 40; K
++)
801 if(KeepFDs
.find(K
) == KeepFDs
.end())
802 fcntl(K
,F_SETFD
,FD_CLOEXEC
);
809 // ExecWait - Fancy waitpid /*{{{*/
810 // ---------------------------------------------------------------------
811 /* Waits for the given sub process. If Reap is set then no errors are
812 generated. Otherwise a failed subprocess will generate a proper descriptive
814 bool ExecWait(pid_t Pid
,const char *Name
,bool Reap
)
819 // Wait and collect the error code
821 while (waitpid(Pid
,&Status
,0) != Pid
)
829 return _error
->Error(_("Waited for %s but it wasn't there"),Name
);
833 // Check for an error code.
834 if (WIFEXITED(Status
) == 0 || WEXITSTATUS(Status
) != 0)
838 if (WIFSIGNALED(Status
) != 0)
840 if( WTERMSIG(Status
) == SIGSEGV
)
841 return _error
->Error(_("Sub-process %s received a segmentation fault."),Name
);
843 return _error
->Error(_("Sub-process %s received signal %u."),Name
, WTERMSIG(Status
));
846 if (WIFEXITED(Status
) != 0)
847 return _error
->Error(_("Sub-process %s returned an error code (%u)"),Name
,WEXITSTATUS(Status
));
849 return _error
->Error(_("Sub-process %s exited unexpectedly"),Name
);
856 // StartsWithGPGClearTextSignature - Check if a file is Pgp/GPG clearsigned /*{{{*/
857 // ---------------------------------------------------------------------
859 bool StartsWithGPGClearTextSignature(string
const &FileName
)
861 static const char* SIGMSG
= "-----BEGIN PGP SIGNED MESSAGE-----\n";
862 char buffer
[strlen(SIGMSG
)+1];
863 FILE* gpg
= fopen(FileName
.c_str(), "r");
867 char const * const test
= fgets(buffer
, sizeof(buffer
), gpg
);
869 if (test
== NULL
|| strcmp(buffer
, SIGMSG
) != 0)
876 // FileFd::Open - Open a file /*{{{*/
877 // ---------------------------------------------------------------------
878 /* The most commonly used open mode combinations are given with Mode */
879 bool FileFd::Open(string FileName
,unsigned int const Mode
,CompressMode Compress
, unsigned long const Perms
)
881 if (Mode
== ReadOnlyGzip
)
882 return Open(FileName
, ReadOnly
, Gzip
, Perms
);
884 if (Compress
== Auto
&& (Mode
& WriteOnly
) == WriteOnly
)
885 return FileFdError("Autodetection on %s only works in ReadOnly openmode!", FileName
.c_str());
887 std::vector
<APT::Configuration::Compressor
> const compressors
= APT::Configuration::getCompressors();
888 std::vector
<APT::Configuration::Compressor
>::const_iterator compressor
= compressors
.begin();
889 if (Compress
== Auto
)
891 for (; compressor
!= compressors
.end(); ++compressor
)
893 std::string file
= std::string(FileName
).append(compressor
->Extension
);
894 if (FileExists(file
) == false)
900 else if (Compress
== Extension
)
902 std::string::size_type
const found
= FileName
.find_last_of('.');
904 if (found
!= std::string::npos
)
906 ext
= FileName
.substr(found
);
907 if (ext
== ".new" || ext
== ".bak")
909 std::string::size_type
const found2
= FileName
.find_last_of('.', found
- 1);
910 if (found2
!= std::string::npos
)
911 ext
= FileName
.substr(found2
, found
- found2
);
916 for (; compressor
!= compressors
.end(); ++compressor
)
917 if (ext
== compressor
->Extension
)
919 // no matching extension - assume uncompressed (imagine files like 'example.org_Packages')
920 if (compressor
== compressors
.end())
921 for (compressor
= compressors
.begin(); compressor
!= compressors
.end(); ++compressor
)
922 if (compressor
->Name
== ".")
930 case None
: name
= "."; break;
931 case Gzip
: name
= "gzip"; break;
932 case Bzip2
: name
= "bzip2"; break;
933 case Lzma
: name
= "lzma"; break;
934 case Xz
: name
= "xz"; break;
938 return FileFdError("Opening File %s in None, Auto or Extension should be already handled?!?", FileName
.c_str());
940 for (; compressor
!= compressors
.end(); ++compressor
)
941 if (compressor
->Name
== name
)
943 if (compressor
== compressors
.end())
944 return FileFdError("Can't find a configured compressor %s for file %s", name
.c_str(), FileName
.c_str());
947 if (compressor
== compressors
.end())
948 return FileFdError("Can't find a match for specified compressor mode for file %s", FileName
.c_str());
949 return Open(FileName
, Mode
, *compressor
, Perms
);
951 bool FileFd::Open(string FileName
,unsigned int const Mode
,APT::Configuration::Compressor
const &compressor
, unsigned long const Perms
)
956 if ((Mode
& WriteOnly
) != WriteOnly
&& (Mode
& (Atomic
| Create
| Empty
| Exclusive
)) != 0)
957 return FileFdError("ReadOnly mode for %s doesn't accept additional flags!", FileName
.c_str());
958 if ((Mode
& ReadWrite
) == 0)
959 return FileFdError("No openmode provided in FileFd::Open for %s", FileName
.c_str());
961 if ((Mode
& Atomic
) == Atomic
)
964 char *name
= strdup((FileName
+ ".XXXXXX").c_str());
965 TemporaryFileName
= string(mktemp(name
));
968 else if ((Mode
& (Exclusive
| Create
)) == (Exclusive
| Create
))
970 // for atomic, this will be done by rename in Close()
971 unlink(FileName
.c_str());
973 if ((Mode
& Empty
) == Empty
)
976 if (lstat(FileName
.c_str(),&Buf
) == 0 && S_ISLNK(Buf
.st_mode
))
977 unlink(FileName
.c_str());
981 #define if_FLAGGED_SET(FLAG, MODE) if ((Mode & FLAG) == FLAG) fileflags |= MODE
982 if_FLAGGED_SET(ReadWrite
, O_RDWR
);
983 else if_FLAGGED_SET(ReadOnly
, O_RDONLY
);
984 else if_FLAGGED_SET(WriteOnly
, O_WRONLY
);
986 if_FLAGGED_SET(Create
, O_CREAT
);
987 if_FLAGGED_SET(Empty
, O_TRUNC
);
988 if_FLAGGED_SET(Exclusive
, O_EXCL
);
989 else if_FLAGGED_SET(Atomic
, O_EXCL
);
990 #undef if_FLAGGED_SET
992 if (TemporaryFileName
.empty() == false)
993 iFd
= open(TemporaryFileName
.c_str(), fileflags
, Perms
);
995 iFd
= open(FileName
.c_str(), fileflags
, Perms
);
997 this->FileName
= FileName
;
998 if (iFd
== -1 || OpenInternDescriptor(Mode
, compressor
) == false)
1005 return FileFdErrno("open",_("Could not open file %s"), FileName
.c_str());
1008 SetCloseExec(iFd
,true);
1012 // FileFd::OpenDescriptor - Open a filedescriptor /*{{{*/
1013 // ---------------------------------------------------------------------
1015 bool FileFd::OpenDescriptor(int Fd
, unsigned int const Mode
, CompressMode Compress
, bool AutoClose
)
1017 std::vector
<APT::Configuration::Compressor
> const compressors
= APT::Configuration::getCompressors();
1018 std::vector
<APT::Configuration::Compressor
>::const_iterator compressor
= compressors
.begin();
1021 // compat with the old API
1022 if (Mode
== ReadOnlyGzip
&& Compress
== None
)
1027 case None
: name
= "."; break;
1028 case Gzip
: name
= "gzip"; break;
1029 case Bzip2
: name
= "bzip2"; break;
1030 case Lzma
: name
= "lzma"; break;
1031 case Xz
: name
= "xz"; break;
1034 if (AutoClose
== true && Fd
!= -1)
1036 return FileFdError("Opening Fd %d in Auto or Extension compression mode is not supported", Fd
);
1038 for (; compressor
!= compressors
.end(); ++compressor
)
1039 if (compressor
->Name
== name
)
1041 if (compressor
== compressors
.end())
1043 if (AutoClose
== true && Fd
!= -1)
1045 return FileFdError("Can't find a configured compressor %s for file %s", name
.c_str(), FileName
.c_str());
1047 return OpenDescriptor(Fd
, Mode
, *compressor
, AutoClose
);
1049 bool FileFd::OpenDescriptor(int Fd
, unsigned int const Mode
, APT::Configuration::Compressor
const &compressor
, bool AutoClose
)
1052 Flags
= (AutoClose
) ? FileFd::AutoClose
: 0;
1053 if (AutoClose
== false && (
1055 compressor
.Name
== "gzip" ||
1058 compressor
.Name
== "bzip2" ||
1062 // Need to duplicate fd here or gzclose for cleanup will close the fd as well
1067 this->FileName
= "";
1068 if (Fd
== -1 || OpenInternDescriptor(Mode
, compressor
) == false)
1072 compressor
.Name
== "gzip" ||
1075 compressor
.Name
== "bzip2" ||
1082 return FileFdError(_("Could not open file descriptor %d"), Fd
);
1086 bool FileFd::OpenInternDescriptor(unsigned int const Mode
, APT::Configuration::Compressor
const &compressor
)
1088 if (compressor
.Name
== "." || compressor
.Binary
.empty() == true)
1093 d
= new FileFdPrivate();
1095 d
->compressor
= compressor
;
1099 if (compressor
.Name
== "gzip")
1106 if ((Mode
& ReadWrite
) == ReadWrite
)
1107 d
->gz
= gzdopen(iFd
, "r+");
1108 else if ((Mode
& WriteOnly
) == WriteOnly
)
1109 d
->gz
= gzdopen(iFd
, "w");
1111 d
->gz
= gzdopen(iFd
, "r");
1114 Flags
|= Compressed
;
1119 if (compressor
.Name
== "bzip2")
1123 BZ2_bzclose(d
->bz2
);
1126 if ((Mode
& ReadWrite
) == ReadWrite
)
1127 d
->bz2
= BZ2_bzdopen(iFd
, "r+");
1128 else if ((Mode
& WriteOnly
) == WriteOnly
)
1129 d
->bz2
= BZ2_bzdopen(iFd
, "w");
1131 d
->bz2
= BZ2_bzdopen(iFd
, "r");
1134 Flags
|= Compressed
;
1139 // collect zombies here in case we reopen
1140 if (d
->compressor_pid
> 0)
1141 ExecWait(d
->compressor_pid
, "FileFdCompressor", true);
1143 if ((Mode
& ReadWrite
) == ReadWrite
)
1144 return FileFdError("ReadWrite mode is not supported for file %s", FileName
.c_str());
1146 bool const Comp
= (Mode
& WriteOnly
) == WriteOnly
;
1149 // Handle 'decompression' of empty files
1152 if (Buf
.st_size
== 0 && S_ISFIFO(Buf
.st_mode
) == false)
1155 // We don't need the file open - instead let the compressor open it
1156 // as he properly knows better how to efficiently read from 'his' file
1157 if (FileName
.empty() == false)
1164 // Create a data pipe
1165 int Pipe
[2] = {-1,-1};
1166 if (pipe(Pipe
) != 0)
1167 return FileFdErrno("pipe",_("Failed to create subprocess IPC"));
1168 for (int J
= 0; J
!= 2; J
++)
1169 SetCloseExec(Pipe
[J
],true);
1171 d
->compressed_fd
= iFd
;
1180 d
->compressor_pid
= ExecFork();
1181 if (d
->compressor_pid
== 0)
1185 dup2(d
->compressed_fd
,STDOUT_FILENO
);
1186 dup2(Pipe
[0],STDIN_FILENO
);
1190 if (FileName
.empty() == true)
1191 dup2(d
->compressed_fd
,STDIN_FILENO
);
1192 dup2(Pipe
[1],STDOUT_FILENO
);
1194 int const nullfd
= open("/dev/null", O_WRONLY
);
1197 dup2(nullfd
,STDERR_FILENO
);
1201 SetCloseExec(STDOUT_FILENO
,false);
1202 SetCloseExec(STDIN_FILENO
,false);
1204 std::vector
<char const*> Args
;
1205 Args
.push_back(compressor
.Binary
.c_str());
1206 std::vector
<std::string
> const * const addArgs
=
1207 (Comp
== true) ? &(compressor
.CompressArgs
) : &(compressor
.UncompressArgs
);
1208 for (std::vector
<std::string
>::const_iterator a
= addArgs
->begin();
1209 a
!= addArgs
->end(); ++a
)
1210 Args
.push_back(a
->c_str());
1211 if (Comp
== false && FileName
.empty() == false)
1213 Args
.push_back("--stdout");
1214 if (TemporaryFileName
.empty() == false)
1215 Args
.push_back(TemporaryFileName
.c_str());
1217 Args
.push_back(FileName
.c_str());
1219 Args
.push_back(NULL
);
1221 execvp(Args
[0],(char **)&Args
[0]);
1222 cerr
<< _("Failed to exec compressor ") << Args
[0] << endl
;
1233 // FileFd::~File - Closes the file /*{{{*/
1234 // ---------------------------------------------------------------------
1235 /* If the proper modes are selected then we close the Fd and possibly
1236 unlink the file on error. */
1242 d
->CloseDown(FileName
);
1248 // FileFd::Read - Read a bit of the file /*{{{*/
1249 // ---------------------------------------------------------------------
1250 /* We are carefull to handle interruption by a signal while reading
1252 bool FileFd::Read(void *To
,unsigned long long Size
,unsigned long long *Actual
)
1258 *((char *)To
) = '\0';
1262 if (d
!= NULL
&& d
->gz
!= NULL
)
1263 Res
= gzread(d
->gz
,To
,Size
);
1267 if (d
!= NULL
&& d
->bz2
!= NULL
)
1268 Res
= BZ2_bzread(d
->bz2
,To
,Size
);
1271 Res
= read(iFd
,To
,Size
);
1278 if (d
!= NULL
&& d
->gz
!= NULL
)
1281 char const * const errmsg
= gzerror(d
->gz
, &err
);
1283 return FileFdError("gzread: %s (%d: %s)", _("Read error"), err
, errmsg
);
1287 if (d
!= NULL
&& d
->bz2
!= NULL
)
1290 char const * const errmsg
= BZ2_bzerror(d
->bz2
, &err
);
1291 if (err
!= BZ_IO_ERROR
)
1292 return FileFdError("BZ2_bzread: %s (%d: %s)", _("Read error"), err
, errmsg
);
1295 return FileFdErrno("read",_("Read error"));
1298 To
= (char *)To
+ Res
;
1305 while (Res
> 0 && Size
> 0);
1317 return FileFdError(_("read, still have %llu to read but none left"), Size
);
1320 // FileFd::ReadLine - Read a complete line from the file /*{{{*/
1321 // ---------------------------------------------------------------------
1322 /* Beware: This method can be quiet slow for big buffers on UNcompressed
1323 files because of the naive implementation! */
1324 char* FileFd::ReadLine(char *To
, unsigned long long const Size
)
1328 if (d
!= NULL
&& d
->gz
!= NULL
)
1329 return gzgets(d
->gz
, To
, Size
);
1332 unsigned long long read
= 0;
1333 while ((Size
- 1) != read
)
1335 unsigned long long done
= 0;
1336 if (Read(To
+ read
, 1, &done
) == false)
1340 if (To
[read
++] == '\n')
1349 // FileFd::Write - Write to the file /*{{{*/
1350 // ---------------------------------------------------------------------
1352 bool FileFd::Write(const void *From
,unsigned long long Size
)
1359 if (d
!= NULL
&& d
->gz
!= NULL
)
1360 Res
= gzwrite(d
->gz
,From
,Size
);
1364 if (d
!= NULL
&& d
->bz2
!= NULL
)
1365 Res
= BZ2_bzwrite(d
->bz2
,(void*)From
,Size
);
1368 Res
= write(iFd
,From
,Size
);
1369 if (Res
< 0 && errno
== EINTR
)
1374 if (d
!= NULL
&& d
->gz
!= NULL
)
1377 char const * const errmsg
= gzerror(d
->gz
, &err
);
1379 return FileFdError("gzwrite: %s (%d: %s)", _("Write error"), err
, errmsg
);
1383 if (d
!= NULL
&& d
->bz2
!= NULL
)
1386 char const * const errmsg
= BZ2_bzerror(d
->bz2
, &err
);
1387 if (err
!= BZ_IO_ERROR
)
1388 return FileFdError("BZ2_bzwrite: %s (%d: %s)", _("Write error"), err
, errmsg
);
1391 return FileFdErrno("write",_("Write error"));
1394 From
= (char *)From
+ Res
;
1399 while (Res
> 0 && Size
> 0);
1404 return FileFdError(_("write, still have %llu to write but couldn't"), Size
);
1406 bool FileFd::Write(int Fd
, const void *From
, unsigned long long Size
)
1412 Res
= write(Fd
,From
,Size
);
1413 if (Res
< 0 && errno
== EINTR
)
1416 return _error
->Errno("write",_("Write error"));
1418 From
= (char *)From
+ Res
;
1421 while (Res
> 0 && Size
> 0);
1426 return _error
->Error(_("write, still have %llu to write but couldn't"), Size
);
1429 // FileFd::Seek - Seek in the file /*{{{*/
1430 // ---------------------------------------------------------------------
1432 bool FileFd::Seek(unsigned long long To
)
1434 if (d
!= NULL
&& (d
->pipe
== true
1440 // Our poor man seeking in pipes is costly, so try to avoid it
1441 unsigned long long seekpos
= Tell();
1444 else if (seekpos
< To
)
1445 return Skip(To
- seekpos
);
1447 if ((d
->openmode
& ReadOnly
) != ReadOnly
)
1448 return FileFdError("Reopen is only implemented for read-only files!");
1452 BZ2_bzclose(d
->bz2
);
1459 if (TemporaryFileName
.empty() == false)
1460 iFd
= open(TemporaryFileName
.c_str(), O_RDONLY
);
1461 else if (FileName
.empty() == false)
1462 iFd
= open(FileName
.c_str(), O_RDONLY
);
1465 if (d
->compressed_fd
> 0)
1466 if (lseek(d
->compressed_fd
, 0, SEEK_SET
) != 0)
1467 iFd
= d
->compressed_fd
;
1469 return FileFdError("Reopen is not implemented for pipes opened with FileFd::OpenDescriptor()!");
1472 if (OpenInternDescriptor(d
->openmode
, d
->compressor
) == false)
1473 return FileFdError("Seek on file %s because it couldn't be reopened", FileName
.c_str());
1483 if (d
!= NULL
&& d
->gz
)
1484 res
= gzseek(d
->gz
,To
,SEEK_SET
);
1487 res
= lseek(iFd
,To
,SEEK_SET
);
1488 if (res
!= (signed)To
)
1489 return FileFdError("Unable to seek to %llu", To
);
1496 // FileFd::Skip - Seek in the file /*{{{*/
1497 // ---------------------------------------------------------------------
1499 bool FileFd::Skip(unsigned long long Over
)
1501 if (d
!= NULL
&& (d
->pipe
== true
1511 unsigned long long toread
= std::min((unsigned long long) sizeof(buffer
), Over
);
1512 if (Read(buffer
, toread
) == false)
1513 return FileFdError("Unable to seek ahead %llu",Over
);
1521 if (d
!= NULL
&& d
->gz
!= NULL
)
1522 res
= gzseek(d
->gz
,Over
,SEEK_CUR
);
1525 res
= lseek(iFd
,Over
,SEEK_CUR
);
1527 return FileFdError("Unable to seek ahead %llu",Over
);
1534 // FileFd::Truncate - Truncate the file /*{{{*/
1535 // ---------------------------------------------------------------------
1537 bool FileFd::Truncate(unsigned long long To
)
1539 #if defined HAVE_ZLIB || defined HAVE_BZ2
1540 if (d
!= NULL
&& (d
->gz
!= NULL
|| d
->bz2
!= NULL
))
1541 return FileFdError("Truncating compressed files is not implemented (%s)", FileName
.c_str());
1543 if (ftruncate(iFd
,To
) != 0)
1544 return FileFdError("Unable to truncate to %llu",To
);
1549 // FileFd::Tell - Current seek position /*{{{*/
1550 // ---------------------------------------------------------------------
1552 unsigned long long FileFd::Tell()
1554 // In theory, we could just return seekpos here always instead of
1555 // seeking around, but not all users of FileFd use always Seek() and co
1556 // so d->seekpos isn't always true and we can just use it as a hint if
1557 // we have nothing else, but not always as an authority…
1558 if (d
!= NULL
&& (d
->pipe
== true
1567 if (d
!= NULL
&& d
->gz
!= NULL
)
1568 Res
= gztell(d
->gz
);
1571 Res
= lseek(iFd
,0,SEEK_CUR
);
1572 if (Res
== (off_t
)-1)
1573 FileFdErrno("lseek","Failed to determine the current file position");
1579 // FileFd::FileSize - Return the size of the file /*{{{*/
1580 // ---------------------------------------------------------------------
1582 unsigned long long FileFd::FileSize()
1585 if ((d
== NULL
|| d
->pipe
== false) && fstat(iFd
,&Buf
) != 0)
1586 return FileFdErrno("fstat","Unable to determine the file size");
1588 // for compressor pipes st_size is undefined and at 'best' zero
1589 if ((d
!= NULL
&& d
->pipe
== true) || S_ISFIFO(Buf
.st_mode
))
1591 // we set it here, too, as we get the info here for free
1592 // in theory the Open-methods should take care of it already
1595 if (stat(FileName
.c_str(), &Buf
) != 0)
1596 return FileFdErrno("stat","Unable to determine the file size");
1602 // FileFd::Size - Return the size of the content in the file /*{{{*/
1603 // ---------------------------------------------------------------------
1605 unsigned long long FileFd::Size()
1607 unsigned long long size
= FileSize();
1609 // for compressor pipes st_size is undefined and at 'best' zero,
1610 // so we 'read' the content and 'seek' back - see there
1611 if (d
!= NULL
&& (d
->pipe
== true
1613 || (d
->bz2
&& size
> 0)
1617 unsigned long long const oldSeek
= Tell();
1619 unsigned long long read
= 0;
1621 Read(ignore
, sizeof(ignore
), &read
);
1627 // only check gzsize if we are actually a gzip file, just checking for
1628 // "gz" is not sufficient as uncompressed files could be opened with
1629 // gzopen in "direct" mode as well
1630 else if (d
!= NULL
&& d
->gz
&& !gzdirect(d
->gz
) && size
> 0)
1632 off_t
const oldPos
= lseek(iFd
,0,SEEK_CUR
);
1633 /* unfortunately zlib.h doesn't provide a gzsize(), so we have to do
1634 * this ourselves; the original (uncompressed) file size is the last 32
1635 * bits of the file */
1636 // FIXME: Size for gz-files is limited by 32bit… no largefile support
1637 if (lseek(iFd
, -4, SEEK_END
) < 0)
1638 return FileFdErrno("lseek","Unable to seek to end of gzipped file");
1640 if (read(iFd
, &size
, 4) != 4)
1641 return FileFdErrno("read","Unable to read original size of gzipped file");
1643 #ifdef WORDS_BIGENDIAN
1644 uint32_t tmp_size
= size
;
1645 uint8_t const * const p
= (uint8_t const * const) &tmp_size
;
1646 tmp_size
= (p
[3] << 24) | (p
[2] << 16) | (p
[1] << 8) | p
[0];
1650 if (lseek(iFd
, oldPos
, SEEK_SET
) < 0)
1651 return FileFdErrno("lseek","Unable to seek in gzipped file");
1660 // FileFd::ModificationTime - Return the time of last touch /*{{{*/
1661 // ---------------------------------------------------------------------
1663 time_t FileFd::ModificationTime()
1666 if ((d
== NULL
|| d
->pipe
== false) && fstat(iFd
,&Buf
) != 0)
1668 FileFdErrno("fstat","Unable to determine the modification time of file %s", FileName
.c_str());
1672 // for compressor pipes st_size is undefined and at 'best' zero
1673 if ((d
!= NULL
&& d
->pipe
== true) || S_ISFIFO(Buf
.st_mode
))
1675 // we set it here, too, as we get the info here for free
1676 // in theory the Open-methods should take care of it already
1679 if (stat(FileName
.c_str(), &Buf
) != 0)
1681 FileFdErrno("fstat","Unable to determine the modification time of file %s", FileName
.c_str());
1686 return Buf
.st_mtime
;
1689 // FileFd::Close - Close the file if the close flag is set /*{{{*/
1690 // ---------------------------------------------------------------------
1692 bool FileFd::Close()
1698 if ((Flags
& AutoClose
) == AutoClose
)
1700 if ((Flags
& Compressed
) != Compressed
&& iFd
> 0 && close(iFd
) != 0)
1701 Res
&= _error
->Errno("close",_("Problem closing the file %s"), FileName
.c_str());
1705 Res
&= d
->CloseDown(FileName
);
1711 if ((Flags
& Replace
) == Replace
) {
1712 if (rename(TemporaryFileName
.c_str(), FileName
.c_str()) != 0)
1713 Res
&= _error
->Errno("rename",_("Problem renaming the file %s to %s"), TemporaryFileName
.c_str(), FileName
.c_str());
1715 FileName
= TemporaryFileName
; // for the unlink() below.
1716 TemporaryFileName
.clear();
1721 if ((Flags
& Fail
) == Fail
&& (Flags
& DelOnFail
) == DelOnFail
&&
1722 FileName
.empty() == false)
1723 if (unlink(FileName
.c_str()) != 0)
1724 Res
&= _error
->WarningE("unlnk",_("Problem unlinking the file %s"), FileName
.c_str());
1731 // FileFd::Sync - Sync the file /*{{{*/
1732 // ---------------------------------------------------------------------
1736 if (fsync(iFd
) != 0)
1737 return FileFdErrno("sync",_("Problem syncing the file"));
1741 // FileFd::FileFdErrno - set Fail and call _error->Errno *{{{*/
1742 bool FileFd::FileFdErrno(const char *Function
, const char *Description
,...)
1746 size_t msgSize
= 400;
1747 int const errsv
= errno
;
1750 va_start(args
,Description
);
1751 if (_error
->InsertErrno(GlobalError::ERROR
, Function
, Description
, args
, errsv
, msgSize
) == false)
1758 // FileFd::FileFdError - set Fail and call _error->Error *{{{*/
1759 bool FileFd::FileFdError(const char *Description
,...) {
1762 size_t msgSize
= 400;
1765 va_start(args
,Description
);
1766 if (_error
->Insert(GlobalError::ERROR
, Description
, args
, msgSize
) == false)
1774 gzFile
FileFd::gzFd() { return (gzFile
) d
->gz
; }