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. */
1222 d
->CloseDown(FileName
);
1228 // FileFd::Read - Read a bit of the file /*{{{*/
1229 // ---------------------------------------------------------------------
1230 /* We are carefull to handle interruption by a signal while reading
1232 bool FileFd::Read(void *To
,unsigned long long Size
,unsigned long long *Actual
)
1238 *((char *)To
) = '\0';
1242 if (d
!= NULL
&& d
->gz
!= NULL
)
1243 Res
= gzread(d
->gz
,To
,Size
);
1247 if (d
!= NULL
&& d
->bz2
!= NULL
)
1248 Res
= BZ2_bzread(d
->bz2
,To
,Size
);
1251 Res
= read(iFd
,To
,Size
);
1258 if (d
!= NULL
&& d
->gz
!= NULL
)
1261 char const * const errmsg
= gzerror(d
->gz
, &err
);
1263 return FileFdError("gzread: %s (%d: %s)", _("Read error"), err
, errmsg
);
1267 if (d
!= NULL
&& d
->bz2
!= NULL
)
1270 char const * const errmsg
= BZ2_bzerror(d
->bz2
, &err
);
1271 if (err
!= BZ_IO_ERROR
)
1272 return FileFdError("BZ2_bzread: %s (%d: %s)", _("Read error"), err
, errmsg
);
1275 return FileFdErrno("read",_("Read error"));
1278 To
= (char *)To
+ Res
;
1285 while (Res
> 0 && Size
> 0);
1297 return FileFdError(_("read, still have %llu to read but none left"), Size
);
1300 // FileFd::ReadLine - Read a complete line from the file /*{{{*/
1301 // ---------------------------------------------------------------------
1302 /* Beware: This method can be quiet slow for big buffers on UNcompressed
1303 files because of the naive implementation! */
1304 char* FileFd::ReadLine(char *To
, unsigned long long const Size
)
1308 if (d
!= NULL
&& d
->gz
!= NULL
)
1309 return gzgets(d
->gz
, To
, Size
);
1312 unsigned long long read
= 0;
1313 while ((Size
- 1) != read
)
1315 unsigned long long done
= 0;
1316 if (Read(To
+ read
, 1, &done
) == false)
1320 if (To
[read
++] == '\n')
1329 // FileFd::Write - Write to the file /*{{{*/
1330 // ---------------------------------------------------------------------
1332 bool FileFd::Write(const void *From
,unsigned long long Size
)
1339 if (d
!= NULL
&& d
->gz
!= NULL
)
1340 Res
= gzwrite(d
->gz
,From
,Size
);
1344 if (d
!= NULL
&& d
->bz2
!= NULL
)
1345 Res
= BZ2_bzwrite(d
->bz2
,(void*)From
,Size
);
1348 Res
= write(iFd
,From
,Size
);
1349 if (Res
< 0 && errno
== EINTR
)
1354 if (d
!= NULL
&& d
->gz
!= NULL
)
1357 char const * const errmsg
= gzerror(d
->gz
, &err
);
1359 return FileFdError("gzwrite: %s (%d: %s)", _("Write error"), err
, errmsg
);
1363 if (d
!= NULL
&& d
->bz2
!= NULL
)
1366 char const * const errmsg
= BZ2_bzerror(d
->bz2
, &err
);
1367 if (err
!= BZ_IO_ERROR
)
1368 return FileFdError("BZ2_bzwrite: %s (%d: %s)", _("Write error"), err
, errmsg
);
1371 return FileFdErrno("write",_("Write error"));
1374 From
= (char *)From
+ Res
;
1379 while (Res
> 0 && Size
> 0);
1384 return FileFdError(_("write, still have %llu to write but couldn't"), Size
);
1386 bool FileFd::Write(int Fd
, const void *From
, unsigned long long Size
)
1392 Res
= write(Fd
,From
,Size
);
1393 if (Res
< 0 && errno
== EINTR
)
1396 return _error
->Errno("write",_("Write error"));
1398 From
= (char *)From
+ Res
;
1401 while (Res
> 0 && Size
> 0);
1406 return _error
->Error(_("write, still have %llu to write but couldn't"), Size
);
1409 // FileFd::Seek - Seek in the file /*{{{*/
1410 // ---------------------------------------------------------------------
1412 bool FileFd::Seek(unsigned long long To
)
1414 if (d
!= NULL
&& (d
->pipe
== true
1420 // Our poor man seeking in pipes is costly, so try to avoid it
1421 unsigned long long seekpos
= Tell();
1424 else if (seekpos
< To
)
1425 return Skip(To
- seekpos
);
1427 if ((d
->openmode
& ReadOnly
) != ReadOnly
)
1428 return FileFdError("Reopen is only implemented for read-only files!");
1432 BZ2_bzclose(d
->bz2
);
1439 if (TemporaryFileName
.empty() == false)
1440 iFd
= open(TemporaryFileName
.c_str(), O_RDONLY
);
1441 else if (FileName
.empty() == false)
1442 iFd
= open(FileName
.c_str(), O_RDONLY
);
1445 if (d
->compressed_fd
> 0)
1446 if (lseek(d
->compressed_fd
, 0, SEEK_SET
) != 0)
1447 iFd
= d
->compressed_fd
;
1449 return FileFdError("Reopen is not implemented for pipes opened with FileFd::OpenDescriptor()!");
1452 if (OpenInternDescriptor(d
->openmode
, d
->compressor
) == false)
1453 return FileFdError("Seek on file %s because it couldn't be reopened", FileName
.c_str());
1463 if (d
!= NULL
&& d
->gz
)
1464 res
= gzseek(d
->gz
,To
,SEEK_SET
);
1467 res
= lseek(iFd
,To
,SEEK_SET
);
1468 if (res
!= (signed)To
)
1469 return FileFdError("Unable to seek to %llu", To
);
1476 // FileFd::Skip - Seek in the file /*{{{*/
1477 // ---------------------------------------------------------------------
1479 bool FileFd::Skip(unsigned long long Over
)
1481 if (d
!= NULL
&& (d
->pipe
== true
1491 unsigned long long toread
= std::min((unsigned long long) sizeof(buffer
), Over
);
1492 if (Read(buffer
, toread
) == false)
1493 return FileFdError("Unable to seek ahead %llu",Over
);
1501 if (d
!= NULL
&& d
->gz
!= NULL
)
1502 res
= gzseek(d
->gz
,Over
,SEEK_CUR
);
1505 res
= lseek(iFd
,Over
,SEEK_CUR
);
1507 return FileFdError("Unable to seek ahead %llu",Over
);
1514 // FileFd::Truncate - Truncate the file /*{{{*/
1515 // ---------------------------------------------------------------------
1517 bool FileFd::Truncate(unsigned long long To
)
1519 #if defined HAVE_ZLIB || defined HAVE_BZ2
1520 if (d
!= NULL
&& (d
->gz
!= NULL
|| d
->bz2
!= NULL
))
1521 return FileFdError("Truncating compressed files is not implemented (%s)", FileName
.c_str());
1523 if (ftruncate(iFd
,To
) != 0)
1524 return FileFdError("Unable to truncate to %llu",To
);
1529 // FileFd::Tell - Current seek position /*{{{*/
1530 // ---------------------------------------------------------------------
1532 unsigned long long FileFd::Tell()
1534 // In theory, we could just return seekpos here always instead of
1535 // seeking around, but not all users of FileFd use always Seek() and co
1536 // so d->seekpos isn't always true and we can just use it as a hint if
1537 // we have nothing else, but not always as an authority…
1538 if (d
!= NULL
&& (d
->pipe
== true
1547 if (d
!= NULL
&& d
->gz
!= NULL
)
1548 Res
= gztell(d
->gz
);
1551 Res
= lseek(iFd
,0,SEEK_CUR
);
1552 if (Res
== (off_t
)-1)
1553 FileFdErrno("lseek","Failed to determine the current file position");
1559 // FileFd::FileSize - Return the size of the file /*{{{*/
1560 // ---------------------------------------------------------------------
1562 unsigned long long FileFd::FileSize()
1565 if ((d
== NULL
|| d
->pipe
== false) && fstat(iFd
,&Buf
) != 0)
1566 return FileFdErrno("fstat","Unable to determine the file size");
1568 // for compressor pipes st_size is undefined and at 'best' zero
1569 if ((d
!= NULL
&& d
->pipe
== true) || S_ISFIFO(Buf
.st_mode
))
1571 // we set it here, too, as we get the info here for free
1572 // in theory the Open-methods should take care of it already
1575 if (stat(FileName
.c_str(), &Buf
) != 0)
1576 return FileFdErrno("stat","Unable to determine the file size");
1582 // FileFd::Size - Return the size of the content in the file /*{{{*/
1583 // ---------------------------------------------------------------------
1585 unsigned long long FileFd::Size()
1587 unsigned long long size
= FileSize();
1589 // for compressor pipes st_size is undefined and at 'best' zero,
1590 // so we 'read' the content and 'seek' back - see there
1591 if (d
!= NULL
&& (d
->pipe
== true
1593 || (d
->bz2
&& size
> 0)
1597 unsigned long long const oldSeek
= Tell();
1599 unsigned long long read
= 0;
1601 if (Read(ignore
, sizeof(ignore
), &read
) == false)
1611 // only check gzsize if we are actually a gzip file, just checking for
1612 // "gz" is not sufficient as uncompressed files could be opened with
1613 // gzopen in "direct" mode as well
1614 else if (d
!= NULL
&& d
->gz
&& !gzdirect(d
->gz
) && size
> 0)
1616 off_t
const oldPos
= lseek(iFd
,0,SEEK_CUR
);
1617 /* unfortunately zlib.h doesn't provide a gzsize(), so we have to do
1618 * this ourselves; the original (uncompressed) file size is the last 32
1619 * bits of the file */
1620 // FIXME: Size for gz-files is limited by 32bit… no largefile support
1621 if (lseek(iFd
, -4, SEEK_END
) < 0)
1623 FileFdErrno("lseek","Unable to seek to end of gzipped file");
1627 if (read(iFd
, &size
, 4) != 4)
1629 FileFdErrno("read","Unable to read original size of gzipped file");
1633 #ifdef WORDS_BIGENDIAN
1634 uint32_t tmp_size
= size
;
1635 uint8_t const * const p
= (uint8_t const * const) &tmp_size
;
1636 tmp_size
= (p
[3] << 24) | (p
[2] << 16) | (p
[1] << 8) | p
[0];
1640 if (lseek(iFd
, oldPos
, SEEK_SET
) < 0)
1642 FileFdErrno("lseek","Unable to seek in gzipped file");
1653 // FileFd::ModificationTime - Return the time of last touch /*{{{*/
1654 // ---------------------------------------------------------------------
1656 time_t FileFd::ModificationTime()
1659 if ((d
== NULL
|| d
->pipe
== false) && fstat(iFd
,&Buf
) != 0)
1661 FileFdErrno("fstat","Unable to determine the modification time of file %s", FileName
.c_str());
1665 // for compressor pipes st_size is undefined and at 'best' zero
1666 if ((d
!= NULL
&& d
->pipe
== true) || S_ISFIFO(Buf
.st_mode
))
1668 // we set it here, too, as we get the info here for free
1669 // in theory the Open-methods should take care of it already
1672 if (stat(FileName
.c_str(), &Buf
) != 0)
1674 FileFdErrno("fstat","Unable to determine the modification time of file %s", FileName
.c_str());
1679 return Buf
.st_mtime
;
1682 // FileFd::Close - Close the file if the close flag is set /*{{{*/
1683 // ---------------------------------------------------------------------
1685 bool FileFd::Close()
1691 if ((Flags
& AutoClose
) == AutoClose
)
1693 if ((Flags
& Compressed
) != Compressed
&& iFd
> 0 && close(iFd
) != 0)
1694 Res
&= _error
->Errno("close",_("Problem closing the file %s"), FileName
.c_str());
1698 Res
&= d
->CloseDown(FileName
);
1704 if ((Flags
& Replace
) == Replace
) {
1705 if (rename(TemporaryFileName
.c_str(), FileName
.c_str()) != 0)
1706 Res
&= _error
->Errno("rename",_("Problem renaming the file %s to %s"), TemporaryFileName
.c_str(), FileName
.c_str());
1708 FileName
= TemporaryFileName
; // for the unlink() below.
1709 TemporaryFileName
.clear();
1714 if ((Flags
& Fail
) == Fail
&& (Flags
& DelOnFail
) == DelOnFail
&&
1715 FileName
.empty() == false)
1716 if (unlink(FileName
.c_str()) != 0)
1717 Res
&= _error
->WarningE("unlnk",_("Problem unlinking the file %s"), FileName
.c_str());
1724 // FileFd::Sync - Sync the file /*{{{*/
1725 // ---------------------------------------------------------------------
1729 if (fsync(iFd
) != 0)
1730 return FileFdErrno("sync",_("Problem syncing the file"));
1734 // FileFd::FileFdErrno - set Fail and call _error->Errno *{{{*/
1735 bool FileFd::FileFdErrno(const char *Function
, const char *Description
,...)
1739 size_t msgSize
= 400;
1740 int const errsv
= errno
;
1743 va_start(args
,Description
);
1744 if (_error
->InsertErrno(GlobalError::ERROR
, Function
, Description
, args
, errsv
, msgSize
) == false)
1751 // FileFd::FileFdError - set Fail and call _error->Error *{{{*/
1752 bool FileFd::FileFdError(const char *Description
,...) {
1755 size_t msgSize
= 400;
1758 va_start(args
,Description
);
1759 if (_error
->Insert(GlobalError::ERROR
, Description
, args
, msgSize
) == false)
1767 gzFile
FileFd::gzFd() { return (gzFile
) d
->gz
; }