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 // FileFd::Open - Open a file /*{{{*/
857 // ---------------------------------------------------------------------
858 /* The most commonly used open mode combinations are given with Mode */
859 bool FileFd::Open(string FileName
,unsigned int const Mode
,CompressMode Compress
, unsigned long const Perms
)
861 if (Mode
== ReadOnlyGzip
)
862 return Open(FileName
, ReadOnly
, Gzip
, Perms
);
864 if (Compress
== Auto
&& (Mode
& WriteOnly
) == WriteOnly
)
865 return FileFdError("Autodetection on %s only works in ReadOnly openmode!", FileName
.c_str());
867 std::vector
<APT::Configuration::Compressor
> const compressors
= APT::Configuration::getCompressors();
868 std::vector
<APT::Configuration::Compressor
>::const_iterator compressor
= compressors
.begin();
869 if (Compress
== Auto
)
871 for (; compressor
!= compressors
.end(); ++compressor
)
873 std::string file
= std::string(FileName
).append(compressor
->Extension
);
874 if (FileExists(file
) == false)
880 else if (Compress
== Extension
)
882 std::string::size_type
const found
= FileName
.find_last_of('.');
884 if (found
!= std::string::npos
)
886 ext
= FileName
.substr(found
);
887 if (ext
== ".new" || ext
== ".bak")
889 std::string::size_type
const found2
= FileName
.find_last_of('.', found
- 1);
890 if (found2
!= std::string::npos
)
891 ext
= FileName
.substr(found2
, found
- found2
);
896 for (; compressor
!= compressors
.end(); ++compressor
)
897 if (ext
== compressor
->Extension
)
899 // no matching extension - assume uncompressed (imagine files like 'example.org_Packages')
900 if (compressor
== compressors
.end())
901 for (compressor
= compressors
.begin(); compressor
!= compressors
.end(); ++compressor
)
902 if (compressor
->Name
== ".")
910 case None
: name
= "."; break;
911 case Gzip
: name
= "gzip"; break;
912 case Bzip2
: name
= "bzip2"; break;
913 case Lzma
: name
= "lzma"; break;
914 case Xz
: name
= "xz"; break;
918 return FileFdError("Opening File %s in None, Auto or Extension should be already handled?!?", FileName
.c_str());
920 for (; compressor
!= compressors
.end(); ++compressor
)
921 if (compressor
->Name
== name
)
923 if (compressor
== compressors
.end())
924 return FileFdError("Can't find a configured compressor %s for file %s", name
.c_str(), FileName
.c_str());
927 if (compressor
== compressors
.end())
928 return FileFdError("Can't find a match for specified compressor mode for file %s", FileName
.c_str());
929 return Open(FileName
, Mode
, *compressor
, Perms
);
931 bool FileFd::Open(string FileName
,unsigned int const Mode
,APT::Configuration::Compressor
const &compressor
, unsigned long const Perms
)
936 if ((Mode
& WriteOnly
) != WriteOnly
&& (Mode
& (Atomic
| Create
| Empty
| Exclusive
)) != 0)
937 return FileFdError("ReadOnly mode for %s doesn't accept additional flags!", FileName
.c_str());
938 if ((Mode
& ReadWrite
) == 0)
939 return FileFdError("No openmode provided in FileFd::Open for %s", FileName
.c_str());
941 if ((Mode
& Atomic
) == Atomic
)
944 char *name
= strdup((FileName
+ ".XXXXXX").c_str());
945 TemporaryFileName
= string(mktemp(name
));
948 else if ((Mode
& (Exclusive
| Create
)) == (Exclusive
| Create
))
950 // for atomic, this will be done by rename in Close()
951 unlink(FileName
.c_str());
953 if ((Mode
& Empty
) == Empty
)
956 if (lstat(FileName
.c_str(),&Buf
) == 0 && S_ISLNK(Buf
.st_mode
))
957 unlink(FileName
.c_str());
961 #define if_FLAGGED_SET(FLAG, MODE) if ((Mode & FLAG) == FLAG) fileflags |= MODE
962 if_FLAGGED_SET(ReadWrite
, O_RDWR
);
963 else if_FLAGGED_SET(ReadOnly
, O_RDONLY
);
964 else if_FLAGGED_SET(WriteOnly
, O_WRONLY
);
966 if_FLAGGED_SET(Create
, O_CREAT
);
967 if_FLAGGED_SET(Empty
, O_TRUNC
);
968 if_FLAGGED_SET(Exclusive
, O_EXCL
);
969 else if_FLAGGED_SET(Atomic
, O_EXCL
);
970 #undef if_FLAGGED_SET
972 if (TemporaryFileName
.empty() == false)
973 iFd
= open(TemporaryFileName
.c_str(), fileflags
, Perms
);
975 iFd
= open(FileName
.c_str(), fileflags
, Perms
);
977 this->FileName
= FileName
;
978 if (iFd
== -1 || OpenInternDescriptor(Mode
, compressor
) == false)
985 return FileFdErrno("open",_("Could not open file %s"), FileName
.c_str());
988 SetCloseExec(iFd
,true);
992 // FileFd::OpenDescriptor - Open a filedescriptor /*{{{*/
993 // ---------------------------------------------------------------------
995 bool FileFd::OpenDescriptor(int Fd
, unsigned int const Mode
, CompressMode Compress
, bool AutoClose
)
997 std::vector
<APT::Configuration::Compressor
> const compressors
= APT::Configuration::getCompressors();
998 std::vector
<APT::Configuration::Compressor
>::const_iterator compressor
= compressors
.begin();
1001 // compat with the old API
1002 if (Mode
== ReadOnlyGzip
&& Compress
== None
)
1007 case None
: name
= "."; break;
1008 case Gzip
: name
= "gzip"; break;
1009 case Bzip2
: name
= "bzip2"; break;
1010 case Lzma
: name
= "lzma"; break;
1011 case Xz
: name
= "xz"; break;
1014 if (AutoClose
== true && Fd
!= -1)
1016 return FileFdError("Opening Fd %d in Auto or Extension compression mode is not supported", Fd
);
1018 for (; compressor
!= compressors
.end(); ++compressor
)
1019 if (compressor
->Name
== name
)
1021 if (compressor
== compressors
.end())
1023 if (AutoClose
== true && Fd
!= -1)
1025 return FileFdError("Can't find a configured compressor %s for file %s", name
.c_str(), FileName
.c_str());
1027 return OpenDescriptor(Fd
, Mode
, *compressor
, AutoClose
);
1029 bool FileFd::OpenDescriptor(int Fd
, unsigned int const Mode
, APT::Configuration::Compressor
const &compressor
, bool AutoClose
)
1032 Flags
= (AutoClose
) ? FileFd::AutoClose
: 0;
1033 if (AutoClose
== false && (
1035 compressor
.Name
== "gzip" ||
1038 compressor
.Name
== "bzip2" ||
1042 // Need to duplicate fd here or gzclose for cleanup will close the fd as well
1047 this->FileName
= "";
1048 if (Fd
== -1 || OpenInternDescriptor(Mode
, compressor
) == false)
1052 compressor
.Name
== "gzip" ||
1055 compressor
.Name
== "bzip2" ||
1062 return FileFdError(_("Could not open file descriptor %d"), Fd
);
1066 bool FileFd::OpenInternDescriptor(unsigned int const Mode
, APT::Configuration::Compressor
const &compressor
)
1068 if (compressor
.Name
== "." || compressor
.Binary
.empty() == true)
1073 d
= new FileFdPrivate();
1075 d
->compressor
= compressor
;
1079 if (compressor
.Name
== "gzip")
1086 if ((Mode
& ReadWrite
) == ReadWrite
)
1087 d
->gz
= gzdopen(iFd
, "r+");
1088 else if ((Mode
& WriteOnly
) == WriteOnly
)
1089 d
->gz
= gzdopen(iFd
, "w");
1091 d
->gz
= gzdopen(iFd
, "r");
1094 Flags
|= Compressed
;
1099 if (compressor
.Name
== "bzip2")
1103 BZ2_bzclose(d
->bz2
);
1106 if ((Mode
& ReadWrite
) == ReadWrite
)
1107 d
->bz2
= BZ2_bzdopen(iFd
, "r+");
1108 else if ((Mode
& WriteOnly
) == WriteOnly
)
1109 d
->bz2
= BZ2_bzdopen(iFd
, "w");
1111 d
->bz2
= BZ2_bzdopen(iFd
, "r");
1114 Flags
|= Compressed
;
1119 // collect zombies here in case we reopen
1120 if (d
->compressor_pid
> 0)
1121 ExecWait(d
->compressor_pid
, "FileFdCompressor", true);
1123 if ((Mode
& ReadWrite
) == ReadWrite
)
1124 return FileFdError("ReadWrite mode is not supported for file %s", FileName
.c_str());
1126 bool const Comp
= (Mode
& WriteOnly
) == WriteOnly
;
1129 // Handle 'decompression' of empty files
1132 if (Buf
.st_size
== 0 && S_ISFIFO(Buf
.st_mode
) == false)
1135 // We don't need the file open - instead let the compressor open it
1136 // as he properly knows better how to efficiently read from 'his' file
1137 if (FileName
.empty() == false)
1144 // Create a data pipe
1145 int Pipe
[2] = {-1,-1};
1146 if (pipe(Pipe
) != 0)
1147 return FileFdErrno("pipe",_("Failed to create subprocess IPC"));
1148 for (int J
= 0; J
!= 2; J
++)
1149 SetCloseExec(Pipe
[J
],true);
1151 d
->compressed_fd
= iFd
;
1160 d
->compressor_pid
= ExecFork();
1161 if (d
->compressor_pid
== 0)
1165 dup2(d
->compressed_fd
,STDOUT_FILENO
);
1166 dup2(Pipe
[0],STDIN_FILENO
);
1170 if (FileName
.empty() == true)
1171 dup2(d
->compressed_fd
,STDIN_FILENO
);
1172 dup2(Pipe
[1],STDOUT_FILENO
);
1174 int const nullfd
= open("/dev/null", O_WRONLY
);
1177 dup2(nullfd
,STDERR_FILENO
);
1181 SetCloseExec(STDOUT_FILENO
,false);
1182 SetCloseExec(STDIN_FILENO
,false);
1184 std::vector
<char const*> Args
;
1185 Args
.push_back(compressor
.Binary
.c_str());
1186 std::vector
<std::string
> const * const addArgs
=
1187 (Comp
== true) ? &(compressor
.CompressArgs
) : &(compressor
.UncompressArgs
);
1188 for (std::vector
<std::string
>::const_iterator a
= addArgs
->begin();
1189 a
!= addArgs
->end(); ++a
)
1190 Args
.push_back(a
->c_str());
1191 if (Comp
== false && FileName
.empty() == false)
1193 Args
.push_back("--stdout");
1194 if (TemporaryFileName
.empty() == false)
1195 Args
.push_back(TemporaryFileName
.c_str());
1197 Args
.push_back(FileName
.c_str());
1199 Args
.push_back(NULL
);
1201 execvp(Args
[0],(char **)&Args
[0]);
1202 cerr
<< _("Failed to exec compressor ") << Args
[0] << endl
;
1213 // FileFd::~File - Closes the file /*{{{*/
1214 // ---------------------------------------------------------------------
1215 /* If the proper modes are selected then we close the Fd and possibly
1216 unlink the file on error. */
1221 d
->CloseDown(FileName
);
1226 // FileFd::Read - Read a bit of the file /*{{{*/
1227 // ---------------------------------------------------------------------
1228 /* We are carefull to handle interruption by a signal while reading
1230 bool FileFd::Read(void *To
,unsigned long long Size
,unsigned long long *Actual
)
1236 *((char *)To
) = '\0';
1240 if (d
!= NULL
&& d
->gz
!= NULL
)
1241 Res
= gzread(d
->gz
,To
,Size
);
1245 if (d
!= NULL
&& d
->bz2
!= NULL
)
1246 Res
= BZ2_bzread(d
->bz2
,To
,Size
);
1249 Res
= read(iFd
,To
,Size
);
1256 if (d
!= NULL
&& d
->gz
!= NULL
)
1259 char const * const errmsg
= gzerror(d
->gz
, &err
);
1261 return FileFdError("gzread: %s (%d: %s)", _("Read error"), err
, errmsg
);
1265 if (d
!= NULL
&& d
->bz2
!= NULL
)
1268 char const * const errmsg
= BZ2_bzerror(d
->bz2
, &err
);
1269 if (err
!= BZ_IO_ERROR
)
1270 return FileFdError("BZ2_bzread: %s (%d: %s)", _("Read error"), err
, errmsg
);
1273 return FileFdErrno("read",_("Read error"));
1276 To
= (char *)To
+ Res
;
1283 while (Res
> 0 && Size
> 0);
1295 return FileFdError(_("read, still have %llu to read but none left"), Size
);
1298 // FileFd::ReadLine - Read a complete line from the file /*{{{*/
1299 // ---------------------------------------------------------------------
1300 /* Beware: This method can be quiet slow for big buffers on UNcompressed
1301 files because of the naive implementation! */
1302 char* FileFd::ReadLine(char *To
, unsigned long long const Size
)
1306 if (d
!= NULL
&& d
->gz
!= NULL
)
1307 return gzgets(d
->gz
, To
, Size
);
1310 unsigned long long read
= 0;
1311 while ((Size
- 1) != read
)
1313 unsigned long long done
= 0;
1314 if (Read(To
+ read
, 1, &done
) == false)
1318 if (To
[read
++] == '\n')
1327 // FileFd::Write - Write to the file /*{{{*/
1328 // ---------------------------------------------------------------------
1330 bool FileFd::Write(const void *From
,unsigned long long Size
)
1337 if (d
!= NULL
&& d
->gz
!= NULL
)
1338 Res
= gzwrite(d
->gz
,From
,Size
);
1342 if (d
!= NULL
&& d
->bz2
!= NULL
)
1343 Res
= BZ2_bzwrite(d
->bz2
,(void*)From
,Size
);
1346 Res
= write(iFd
,From
,Size
);
1347 if (Res
< 0 && errno
== EINTR
)
1352 if (d
!= NULL
&& d
->gz
!= NULL
)
1355 char const * const errmsg
= gzerror(d
->gz
, &err
);
1357 return FileFdError("gzwrite: %s (%d: %s)", _("Write error"), err
, errmsg
);
1361 if (d
!= NULL
&& d
->bz2
!= NULL
)
1364 char const * const errmsg
= BZ2_bzerror(d
->bz2
, &err
);
1365 if (err
!= BZ_IO_ERROR
)
1366 return FileFdError("BZ2_bzwrite: %s (%d: %s)", _("Write error"), err
, errmsg
);
1369 return FileFdErrno("write",_("Write error"));
1372 From
= (char *)From
+ Res
;
1377 while (Res
> 0 && Size
> 0);
1382 return FileFdError(_("write, still have %llu to write but couldn't"), Size
);
1384 bool FileFd::Write(int Fd
, const void *From
, unsigned long long Size
)
1390 Res
= write(Fd
,From
,Size
);
1391 if (Res
< 0 && errno
== EINTR
)
1394 return _error
->Errno("write",_("Write error"));
1396 From
= (char *)From
+ Res
;
1399 while (Res
> 0 && Size
> 0);
1404 return _error
->Error(_("write, still have %llu to write but couldn't"), Size
);
1407 // FileFd::Seek - Seek in the file /*{{{*/
1408 // ---------------------------------------------------------------------
1410 bool FileFd::Seek(unsigned long long To
)
1412 if (d
!= NULL
&& (d
->pipe
== true
1418 // Our poor man seeking in pipes is costly, so try to avoid it
1419 unsigned long long seekpos
= Tell();
1422 else if (seekpos
< To
)
1423 return Skip(To
- seekpos
);
1425 if ((d
->openmode
& ReadOnly
) != ReadOnly
)
1426 return FileFdError("Reopen is only implemented for read-only files!");
1430 BZ2_bzclose(d
->bz2
);
1437 if (TemporaryFileName
.empty() == false)
1438 iFd
= open(TemporaryFileName
.c_str(), O_RDONLY
);
1439 else if (FileName
.empty() == false)
1440 iFd
= open(FileName
.c_str(), O_RDONLY
);
1443 if (d
->compressed_fd
> 0)
1444 if (lseek(d
->compressed_fd
, 0, SEEK_SET
) != 0)
1445 iFd
= d
->compressed_fd
;
1447 return FileFdError("Reopen is not implemented for pipes opened with FileFd::OpenDescriptor()!");
1450 if (OpenInternDescriptor(d
->openmode
, d
->compressor
) == false)
1451 return FileFdError("Seek on file %s because it couldn't be reopened", FileName
.c_str());
1461 if (d
!= NULL
&& d
->gz
)
1462 res
= gzseek(d
->gz
,To
,SEEK_SET
);
1465 res
= lseek(iFd
,To
,SEEK_SET
);
1466 if (res
!= (signed)To
)
1467 return FileFdError("Unable to seek to %llu", To
);
1474 // FileFd::Skip - Seek in the file /*{{{*/
1475 // ---------------------------------------------------------------------
1477 bool FileFd::Skip(unsigned long long Over
)
1479 if (d
!= NULL
&& (d
->pipe
== true
1489 unsigned long long toread
= std::min((unsigned long long) sizeof(buffer
), Over
);
1490 if (Read(buffer
, toread
) == false)
1491 return FileFdError("Unable to seek ahead %llu",Over
);
1499 if (d
!= NULL
&& d
->gz
!= NULL
)
1500 res
= gzseek(d
->gz
,Over
,SEEK_CUR
);
1503 res
= lseek(iFd
,Over
,SEEK_CUR
);
1505 return FileFdError("Unable to seek ahead %llu",Over
);
1512 // FileFd::Truncate - Truncate the file /*{{{*/
1513 // ---------------------------------------------------------------------
1515 bool FileFd::Truncate(unsigned long long To
)
1517 #if defined HAVE_ZLIB || defined HAVE_BZ2
1518 if (d
!= NULL
&& (d
->gz
!= NULL
|| d
->bz2
!= NULL
))
1519 return FileFdError("Truncating compressed files is not implemented (%s)", FileName
.c_str());
1521 if (ftruncate(iFd
,To
) != 0)
1522 return FileFdError("Unable to truncate to %llu",To
);
1527 // FileFd::Tell - Current seek position /*{{{*/
1528 // ---------------------------------------------------------------------
1530 unsigned long long FileFd::Tell()
1532 // In theory, we could just return seekpos here always instead of
1533 // seeking around, but not all users of FileFd use always Seek() and co
1534 // so d->seekpos isn't always true and we can just use it as a hint if
1535 // we have nothing else, but not always as an authority…
1536 if (d
!= NULL
&& (d
->pipe
== true
1545 if (d
!= NULL
&& d
->gz
!= NULL
)
1546 Res
= gztell(d
->gz
);
1549 Res
= lseek(iFd
,0,SEEK_CUR
);
1550 if (Res
== (off_t
)-1)
1551 FileFdErrno("lseek","Failed to determine the current file position");
1557 // FileFd::FileSize - Return the size of the file /*{{{*/
1558 // ---------------------------------------------------------------------
1560 unsigned long long FileFd::FileSize()
1563 if ((d
== NULL
|| d
->pipe
== false) && fstat(iFd
,&Buf
) != 0)
1564 return FileFdErrno("fstat","Unable to determine the file size");
1566 // for compressor pipes st_size is undefined and at 'best' zero
1567 if ((d
!= NULL
&& d
->pipe
== true) || S_ISFIFO(Buf
.st_mode
))
1569 // we set it here, too, as we get the info here for free
1570 // in theory the Open-methods should take care of it already
1573 if (stat(FileName
.c_str(), &Buf
) != 0)
1574 return FileFdErrno("stat","Unable to determine the file size");
1580 // FileFd::Size - Return the size of the content in the file /*{{{*/
1581 // ---------------------------------------------------------------------
1583 unsigned long long FileFd::Size()
1585 unsigned long long size
= FileSize();
1587 // for compressor pipes st_size is undefined and at 'best' zero,
1588 // so we 'read' the content and 'seek' back - see there
1589 if (d
!= NULL
&& (d
->pipe
== true
1591 || (d
->bz2
&& size
> 0)
1595 unsigned long long const oldSeek
= Tell();
1597 unsigned long long read
= 0;
1599 Read(ignore
, sizeof(ignore
), &read
);
1605 // only check gzsize if we are actually a gzip file, just checking for
1606 // "gz" is not sufficient as uncompressed files could be opened with
1607 // gzopen in "direct" mode as well
1608 else if (d
!= NULL
&& d
->gz
&& !gzdirect(d
->gz
) && size
> 0)
1610 off_t
const oldPos
= lseek(iFd
,0,SEEK_CUR
);
1611 /* unfortunately zlib.h doesn't provide a gzsize(), so we have to do
1612 * this ourselves; the original (uncompressed) file size is the last 32
1613 * bits of the file */
1614 // FIXME: Size for gz-files is limited by 32bit… no largefile support
1615 if (lseek(iFd
, -4, SEEK_END
) < 0)
1616 return FileFdErrno("lseek","Unable to seek to end of gzipped file");
1618 if (read(iFd
, &size
, 4) != 4)
1619 return FileFdErrno("read","Unable to read original size of gzipped file");
1621 #ifdef WORDS_BIGENDIAN
1622 uint32_t tmp_size
= size
;
1623 uint8_t const * const p
= (uint8_t const * const) &tmp_size
;
1624 tmp_size
= (p
[3] << 24) | (p
[2] << 16) | (p
[1] << 8) | p
[0];
1628 if (lseek(iFd
, oldPos
, SEEK_SET
) < 0)
1629 return FileFdErrno("lseek","Unable to seek in gzipped file");
1638 // FileFd::ModificationTime - Return the time of last touch /*{{{*/
1639 // ---------------------------------------------------------------------
1641 time_t FileFd::ModificationTime()
1644 if ((d
== NULL
|| d
->pipe
== false) && fstat(iFd
,&Buf
) != 0)
1646 FileFdErrno("fstat","Unable to determine the modification time of file %s", FileName
.c_str());
1650 // for compressor pipes st_size is undefined and at 'best' zero
1651 if ((d
!= NULL
&& d
->pipe
== true) || S_ISFIFO(Buf
.st_mode
))
1653 // we set it here, too, as we get the info here for free
1654 // in theory the Open-methods should take care of it already
1657 if (stat(FileName
.c_str(), &Buf
) != 0)
1659 FileFdErrno("fstat","Unable to determine the modification time of file %s", FileName
.c_str());
1664 return Buf
.st_mtime
;
1667 // FileFd::Close - Close the file if the close flag is set /*{{{*/
1668 // ---------------------------------------------------------------------
1670 bool FileFd::Close()
1676 if ((Flags
& AutoClose
) == AutoClose
)
1678 if ((Flags
& Compressed
) != Compressed
&& iFd
> 0 && close(iFd
) != 0)
1679 Res
&= _error
->Errno("close",_("Problem closing the file %s"), FileName
.c_str());
1683 Res
&= d
->CloseDown(FileName
);
1689 if ((Flags
& Replace
) == Replace
) {
1690 if (rename(TemporaryFileName
.c_str(), FileName
.c_str()) != 0)
1691 Res
&= _error
->Errno("rename",_("Problem renaming the file %s to %s"), TemporaryFileName
.c_str(), FileName
.c_str());
1693 FileName
= TemporaryFileName
; // for the unlink() below.
1694 TemporaryFileName
.clear();
1699 if ((Flags
& Fail
) == Fail
&& (Flags
& DelOnFail
) == DelOnFail
&&
1700 FileName
.empty() == false)
1701 if (unlink(FileName
.c_str()) != 0)
1702 Res
&= _error
->WarningE("unlnk",_("Problem unlinking the file %s"), FileName
.c_str());
1709 // FileFd::Sync - Sync the file /*{{{*/
1710 // ---------------------------------------------------------------------
1714 if (fsync(iFd
) != 0)
1715 return FileFdErrno("sync",_("Problem syncing the file"));
1719 // FileFd::FileFdErrno - set Fail and call _error->Errno *{{{*/
1720 bool FileFd::FileFdErrno(const char *Function
, const char *Description
,...)
1724 size_t msgSize
= 400;
1725 int const errsv
= errno
;
1728 va_start(args
,Description
);
1729 if (_error
->InsertErrno(GlobalError::ERROR
, Function
, Description
, args
, errsv
, msgSize
) == false)
1736 // FileFd::FileFdError - set Fail and call _error->Error *{{{*/
1737 bool FileFd::FileFdError(const char *Description
,...) {
1740 size_t msgSize
= 400;
1743 va_start(args
,Description
);
1744 if (_error
->Insert(GlobalError::ERROR
, Description
, args
, msgSize
) == false)
1752 gzFile
FileFd::gzFd() { return (gzFile
) d
->gz
; }