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)
247 // always close to not leak resources
255 _error
->Warning(_("Not using locking for nfs mounted lock file %s"),File
.c_str());
256 return dup(0); // Need something for the caller to close
260 _error
->Errno("open",_("Could not get lock %s"),File
.c_str());
268 // FileExists - Check if a file exists /*{{{*/
269 // ---------------------------------------------------------------------
270 /* Beware: Directories are also files! */
271 bool FileExists(string File
)
274 if (stat(File
.c_str(),&Buf
) != 0)
279 // RealFileExists - Check if a file exists and if it is really a file /*{{{*/
280 // ---------------------------------------------------------------------
282 bool RealFileExists(string File
)
285 if (stat(File
.c_str(),&Buf
) != 0)
287 return ((Buf
.st_mode
& S_IFREG
) != 0);
290 // DirectoryExists - Check if a directory exists and is really one /*{{{*/
291 // ---------------------------------------------------------------------
293 bool DirectoryExists(string
const &Path
)
296 if (stat(Path
.c_str(),&Buf
) != 0)
298 return ((Buf
.st_mode
& S_IFDIR
) != 0);
301 // CreateDirectory - poor man's mkdir -p guarded by a parent directory /*{{{*/
302 // ---------------------------------------------------------------------
303 /* This method will create all directories needed for path in good old
304 mkdir -p style but refuses to do this if Parent is not a prefix of
305 this Path. Example: /var/cache/ and /var/cache/apt/archives are given,
306 so it will create apt/archives if /var/cache exists - on the other
307 hand if the parent is /var/lib the creation will fail as this path
308 is not a parent of the path to be generated. */
309 bool CreateDirectory(string
const &Parent
, string
const &Path
)
311 if (Parent
.empty() == true || Path
.empty() == true)
314 if (DirectoryExists(Path
) == true)
317 if (DirectoryExists(Parent
) == false)
320 // we are not going to create directories "into the blue"
321 if (Path
.find(Parent
, 0) != 0)
324 vector
<string
> const dirs
= VectorizeString(Path
.substr(Parent
.size()), '/');
325 string progress
= Parent
;
326 for (vector
<string
>::const_iterator d
= dirs
.begin(); d
!= dirs
.end(); ++d
)
328 if (d
->empty() == true)
331 progress
.append("/").append(*d
);
332 if (DirectoryExists(progress
) == true)
335 if (mkdir(progress
.c_str(), 0755) != 0)
341 // CreateAPTDirectoryIfNeeded - ensure that the given directory exists /*{{{*/
342 // ---------------------------------------------------------------------
343 /* a small wrapper around CreateDirectory to check if it exists and to
344 remove the trailing "/apt/" from the parent directory if needed */
345 bool CreateAPTDirectoryIfNeeded(string
const &Parent
, string
const &Path
)
347 if (DirectoryExists(Path
) == true)
350 size_t const len
= Parent
.size();
351 if (len
> 5 && Parent
.find("/apt/", len
- 6, 5) == len
- 5)
353 if (CreateDirectory(Parent
.substr(0,len
-5), Path
) == true)
356 else if (CreateDirectory(Parent
, Path
) == true)
362 // GetListOfFilesInDir - returns a vector of files in the given dir /*{{{*/
363 // ---------------------------------------------------------------------
364 /* If an extension is given only files with this extension are included
365 in the returned vector, otherwise every "normal" file is included. */
366 std::vector
<string
> GetListOfFilesInDir(string
const &Dir
, string
const &Ext
,
367 bool const &SortList
, bool const &AllowNoExt
)
369 std::vector
<string
> ext
;
371 if (Ext
.empty() == false)
373 if (AllowNoExt
== true && ext
.empty() == false)
375 return GetListOfFilesInDir(Dir
, ext
, SortList
);
377 std::vector
<string
> GetListOfFilesInDir(string
const &Dir
, std::vector
<string
> const &Ext
,
378 bool const &SortList
)
380 // Attention debuggers: need to be set with the environment config file!
381 bool const Debug
= _config
->FindB("Debug::GetListOfFilesInDir", false);
384 std::clog
<< "Accept in " << Dir
<< " only files with the following " << Ext
.size() << " extensions:" << std::endl
;
385 if (Ext
.empty() == true)
386 std::clog
<< "\tNO extension" << std::endl
;
388 for (std::vector
<string
>::const_iterator e
= Ext
.begin();
390 std::clog
<< '\t' << (e
->empty() == true ? "NO" : *e
) << " extension" << std::endl
;
393 std::vector
<string
> List
;
395 if (DirectoryExists(Dir
) == false)
397 _error
->Error(_("List of files can't be created as '%s' is not a directory"), Dir
.c_str());
401 Configuration::MatchAgainstConfig
SilentIgnore("Dir::Ignore-Files-Silently");
402 DIR *D
= opendir(Dir
.c_str());
405 _error
->Errno("opendir",_("Unable to read %s"),Dir
.c_str());
409 for (struct dirent
*Ent
= readdir(D
); Ent
!= 0; Ent
= readdir(D
))
411 // skip "hidden" files
412 if (Ent
->d_name
[0] == '.')
415 // Make sure it is a file and not something else
416 string
const File
= flCombine(Dir
,Ent
->d_name
);
417 #ifdef _DIRENT_HAVE_D_TYPE
418 if (Ent
->d_type
!= DT_REG
)
421 if (RealFileExists(File
) == false)
423 // do not show ignoration warnings for directories
425 #ifdef _DIRENT_HAVE_D_TYPE
426 Ent
->d_type
== DT_DIR
||
428 DirectoryExists(File
) == true)
430 if (SilentIgnore
.Match(Ent
->d_name
) == false)
431 _error
->Notice(_("Ignoring '%s' in directory '%s' as it is not a regular file"), Ent
->d_name
, Dir
.c_str());
436 // check for accepted extension:
437 // no extension given -> periods are bad as hell!
438 // extensions given -> "" extension allows no extension
439 if (Ext
.empty() == false)
441 string d_ext
= flExtension(Ent
->d_name
);
442 if (d_ext
== Ent
->d_name
) // no extension
444 if (std::find(Ext
.begin(), Ext
.end(), "") == Ext
.end())
447 std::clog
<< "Bad file: " << Ent
->d_name
<< " → no extension" << std::endl
;
448 if (SilentIgnore
.Match(Ent
->d_name
) == false)
449 _error
->Notice(_("Ignoring file '%s' in directory '%s' as it has no filename extension"), Ent
->d_name
, Dir
.c_str());
453 else if (std::find(Ext
.begin(), Ext
.end(), d_ext
) == Ext
.end())
456 std::clog
<< "Bad file: " << Ent
->d_name
<< " → bad extension »" << flExtension(Ent
->d_name
) << "«" << std::endl
;
457 if (SilentIgnore
.Match(Ent
->d_name
) == false)
458 _error
->Notice(_("Ignoring file '%s' in directory '%s' as it has an invalid filename extension"), Ent
->d_name
, Dir
.c_str());
463 // Skip bad filenames ala run-parts
464 const char *C
= Ent
->d_name
;
466 if (isalpha(*C
) == 0 && isdigit(*C
) == 0
467 && *C
!= '_' && *C
!= '-') {
468 // no required extension -> dot is a bad character
469 if (*C
== '.' && Ext
.empty() == false)
474 // we don't reach the end of the name -> bad character included
478 std::clog
<< "Bad file: " << Ent
->d_name
<< " → bad character »"
479 << *C
<< "« in filename (period allowed: " << (Ext
.empty() ? "no" : "yes") << ")" << std::endl
;
483 // skip filenames which end with a period. These are never valid
487 std::clog
<< "Bad file: " << Ent
->d_name
<< " → Period as last character" << std::endl
;
492 std::clog
<< "Accept file: " << Ent
->d_name
<< " in " << Dir
<< std::endl
;
493 List
.push_back(File
);
497 if (SortList
== true)
498 std::sort(List
.begin(),List
.end());
501 std::vector
<string
> GetListOfFilesInDir(string
const &Dir
, bool SortList
)
503 bool const Debug
= _config
->FindB("Debug::GetListOfFilesInDir", false);
505 std::clog
<< "Accept in " << Dir
<< " all regular files" << std::endl
;
507 std::vector
<string
> List
;
509 if (DirectoryExists(Dir
) == false)
511 _error
->Error(_("List of files can't be created as '%s' is not a directory"), Dir
.c_str());
515 DIR *D
= opendir(Dir
.c_str());
518 _error
->Errno("opendir",_("Unable to read %s"),Dir
.c_str());
522 for (struct dirent
*Ent
= readdir(D
); Ent
!= 0; Ent
= readdir(D
))
524 // skip "hidden" files
525 if (Ent
->d_name
[0] == '.')
528 // Make sure it is a file and not something else
529 string
const File
= flCombine(Dir
,Ent
->d_name
);
530 #ifdef _DIRENT_HAVE_D_TYPE
531 if (Ent
->d_type
!= DT_REG
)
534 if (RealFileExists(File
) == false)
537 std::clog
<< "Bad file: " << Ent
->d_name
<< " → it is not a real file" << std::endl
;
542 // Skip bad filenames ala run-parts
543 const char *C
= Ent
->d_name
;
545 if (isalpha(*C
) == 0 && isdigit(*C
) == 0
546 && *C
!= '_' && *C
!= '-' && *C
!= '.')
549 // we don't reach the end of the name -> bad character included
553 std::clog
<< "Bad file: " << Ent
->d_name
<< " → bad character »" << *C
<< "« in filename" << std::endl
;
557 // skip filenames which end with a period. These are never valid
561 std::clog
<< "Bad file: " << Ent
->d_name
<< " → Period as last character" << std::endl
;
566 std::clog
<< "Accept file: " << Ent
->d_name
<< " in " << Dir
<< std::endl
;
567 List
.push_back(File
);
571 if (SortList
== true)
572 std::sort(List
.begin(),List
.end());
576 // SafeGetCWD - This is a safer getcwd that returns a dynamic string /*{{{*/
577 // ---------------------------------------------------------------------
578 /* We return / on failure. */
581 // Stash the current dir.
584 if (getcwd(S
,sizeof(S
)-2) == 0)
586 unsigned int Len
= strlen(S
);
592 // GetModificationTime - Get the mtime of the given file or -1 on error /*{{{*/
593 // ---------------------------------------------------------------------
594 /* We return / on failure. */
595 time_t GetModificationTime(string
const &Path
)
598 if (stat(Path
.c_str(), &St
) < 0)
603 // flNotDir - Strip the directory from the filename /*{{{*/
604 // ---------------------------------------------------------------------
606 string
flNotDir(string File
)
608 string::size_type Res
= File
.rfind('/');
609 if (Res
== string::npos
)
612 return string(File
,Res
,Res
- File
.length());
615 // flNotFile - Strip the file from the directory name /*{{{*/
616 // ---------------------------------------------------------------------
617 /* Result ends in a / */
618 string
flNotFile(string File
)
620 string::size_type Res
= File
.rfind('/');
621 if (Res
== string::npos
)
624 return string(File
,0,Res
);
627 // flExtension - Return the extension for the file /*{{{*/
628 // ---------------------------------------------------------------------
630 string
flExtension(string File
)
632 string::size_type Res
= File
.rfind('.');
633 if (Res
== string::npos
)
636 return string(File
,Res
,Res
- File
.length());
639 // flNoLink - If file is a symlink then deref it /*{{{*/
640 // ---------------------------------------------------------------------
641 /* If the name is not a link then the returned path is the input. */
642 string
flNoLink(string File
)
645 if (lstat(File
.c_str(),&St
) != 0 || S_ISLNK(St
.st_mode
) == 0)
647 if (stat(File
.c_str(),&St
) != 0)
650 /* Loop resolving the link. There is no need to limit the number of
651 loops because the stat call above ensures that the symlink is not
659 if ((Res
= readlink(NFile
.c_str(),Buffer
,sizeof(Buffer
))) <= 0 ||
660 (unsigned)Res
>= sizeof(Buffer
))
663 // Append or replace the previous path
665 if (Buffer
[0] == '/')
668 NFile
= flNotFile(NFile
) + Buffer
;
670 // See if we are done
671 if (lstat(NFile
.c_str(),&St
) != 0)
673 if (S_ISLNK(St
.st_mode
) == 0)
678 // flCombine - Combine a file and a directory /*{{{*/
679 // ---------------------------------------------------------------------
680 /* If the file is an absolute path then it is just returned, otherwise
681 the directory is pre-pended to it. */
682 string
flCombine(string Dir
,string File
)
684 if (File
.empty() == true)
687 if (File
[0] == '/' || Dir
.empty() == true)
689 if (File
.length() >= 2 && File
[0] == '.' && File
[1] == '/')
691 if (Dir
[Dir
.length()-1] == '/')
693 return Dir
+ '/' + File
;
696 // SetCloseExec - Set the close on exec flag /*{{{*/
697 // ---------------------------------------------------------------------
699 void SetCloseExec(int Fd
,bool Close
)
701 if (fcntl(Fd
,F_SETFD
,(Close
== false)?0:FD_CLOEXEC
) != 0)
703 cerr
<< "FATAL -> Could not set close on exec " << strerror(errno
) << endl
;
708 // SetNonBlock - Set the nonblocking flag /*{{{*/
709 // ---------------------------------------------------------------------
711 void SetNonBlock(int Fd
,bool Block
)
713 int Flags
= fcntl(Fd
,F_GETFL
) & (~O_NONBLOCK
);
714 if (fcntl(Fd
,F_SETFL
,Flags
| ((Block
== false)?0:O_NONBLOCK
)) != 0)
716 cerr
<< "FATAL -> Could not set non-blocking flag " << strerror(errno
) << endl
;
721 // WaitFd - Wait for a FD to become readable /*{{{*/
722 // ---------------------------------------------------------------------
723 /* This waits for a FD to become readable using select. It is useful for
724 applications making use of non-blocking sockets. The timeout is
726 bool WaitFd(int Fd
,bool write
,unsigned long timeout
)
739 Res
= select(Fd
+1,0,&Set
,0,(timeout
!= 0?&tv
:0));
741 while (Res
< 0 && errno
== EINTR
);
751 Res
= select(Fd
+1,&Set
,0,0,(timeout
!= 0?&tv
:0));
753 while (Res
< 0 && errno
== EINTR
);
762 // ExecFork - Magical fork that sanitizes the context before execing /*{{{*/
763 // ---------------------------------------------------------------------
764 /* This is used if you want to cleanse the environment for the forked
765 child, it fixes up the important signals and nukes all of the fds,
766 otherwise acts like normal fork. */
769 // Fork off the process
770 pid_t Process
= fork();
773 cerr
<< "FATAL -> Failed to fork." << endl
;
777 // Spawn the subprocess
781 signal(SIGPIPE
,SIG_DFL
);
782 signal(SIGQUIT
,SIG_DFL
);
783 signal(SIGINT
,SIG_DFL
);
784 signal(SIGWINCH
,SIG_DFL
);
785 signal(SIGCONT
,SIG_DFL
);
786 signal(SIGTSTP
,SIG_DFL
);
789 Configuration::Item
const *Opts
= _config
->Tree("APT::Keep-Fds");
790 if (Opts
!= 0 && Opts
->Child
!= 0)
793 for (; Opts
!= 0; Opts
= Opts
->Next
)
795 if (Opts
->Value
.empty() == true)
797 int fd
= atoi(Opts
->Value
.c_str());
802 // Close all of our FDs - just in case
803 for (int K
= 3; K
!= 40; K
++)
805 if(KeepFDs
.find(K
) == KeepFDs
.end())
806 fcntl(K
,F_SETFD
,FD_CLOEXEC
);
813 // ExecWait - Fancy waitpid /*{{{*/
814 // ---------------------------------------------------------------------
815 /* Waits for the given sub process. If Reap is set then no errors are
816 generated. Otherwise a failed subprocess will generate a proper descriptive
818 bool ExecWait(pid_t Pid
,const char *Name
,bool Reap
)
823 // Wait and collect the error code
825 while (waitpid(Pid
,&Status
,0) != Pid
)
833 return _error
->Error(_("Waited for %s but it wasn't there"),Name
);
837 // Check for an error code.
838 if (WIFEXITED(Status
) == 0 || WEXITSTATUS(Status
) != 0)
842 if (WIFSIGNALED(Status
) != 0)
844 if( WTERMSIG(Status
) == SIGSEGV
)
845 return _error
->Error(_("Sub-process %s received a segmentation fault."),Name
);
847 return _error
->Error(_("Sub-process %s received signal %u."),Name
, WTERMSIG(Status
));
850 if (WIFEXITED(Status
) != 0)
851 return _error
->Error(_("Sub-process %s returned an error code (%u)"),Name
,WEXITSTATUS(Status
));
853 return _error
->Error(_("Sub-process %s exited unexpectedly"),Name
);
860 // FileFd::Open - Open a file /*{{{*/
861 // ---------------------------------------------------------------------
862 /* The most commonly used open mode combinations are given with Mode */
863 bool FileFd::Open(string FileName
,unsigned int const Mode
,CompressMode Compress
, unsigned long const Perms
)
865 if (Mode
== ReadOnlyGzip
)
866 return Open(FileName
, ReadOnly
, Gzip
, Perms
);
868 if (Compress
== Auto
&& (Mode
& WriteOnly
) == WriteOnly
)
869 return FileFdError("Autodetection on %s only works in ReadOnly openmode!", FileName
.c_str());
871 std::vector
<APT::Configuration::Compressor
> const compressors
= APT::Configuration::getCompressors();
872 std::vector
<APT::Configuration::Compressor
>::const_iterator compressor
= compressors
.begin();
873 if (Compress
== Auto
)
875 for (; compressor
!= compressors
.end(); ++compressor
)
877 std::string file
= std::string(FileName
).append(compressor
->Extension
);
878 if (FileExists(file
) == false)
884 else if (Compress
== Extension
)
886 std::string::size_type
const found
= FileName
.find_last_of('.');
888 if (found
!= std::string::npos
)
890 ext
= FileName
.substr(found
);
891 if (ext
== ".new" || ext
== ".bak")
893 std::string::size_type
const found2
= FileName
.find_last_of('.', found
- 1);
894 if (found2
!= std::string::npos
)
895 ext
= FileName
.substr(found2
, found
- found2
);
900 for (; compressor
!= compressors
.end(); ++compressor
)
901 if (ext
== compressor
->Extension
)
903 // no matching extension - assume uncompressed (imagine files like 'example.org_Packages')
904 if (compressor
== compressors
.end())
905 for (compressor
= compressors
.begin(); compressor
!= compressors
.end(); ++compressor
)
906 if (compressor
->Name
== ".")
914 case None
: name
= "."; break;
915 case Gzip
: name
= "gzip"; break;
916 case Bzip2
: name
= "bzip2"; break;
917 case Lzma
: name
= "lzma"; break;
918 case Xz
: name
= "xz"; break;
922 return FileFdError("Opening File %s in None, Auto or Extension should be already handled?!?", FileName
.c_str());
924 for (; compressor
!= compressors
.end(); ++compressor
)
925 if (compressor
->Name
== name
)
927 if (compressor
== compressors
.end())
928 return FileFdError("Can't find a configured compressor %s for file %s", name
.c_str(), FileName
.c_str());
931 if (compressor
== compressors
.end())
932 return FileFdError("Can't find a match for specified compressor mode for file %s", FileName
.c_str());
933 return Open(FileName
, Mode
, *compressor
, Perms
);
935 bool FileFd::Open(string FileName
,unsigned int const Mode
,APT::Configuration::Compressor
const &compressor
, unsigned long const Perms
)
940 if ((Mode
& WriteOnly
) != WriteOnly
&& (Mode
& (Atomic
| Create
| Empty
| Exclusive
)) != 0)
941 return FileFdError("ReadOnly mode for %s doesn't accept additional flags!", FileName
.c_str());
942 if ((Mode
& ReadWrite
) == 0)
943 return FileFdError("No openmode provided in FileFd::Open for %s", FileName
.c_str());
945 if ((Mode
& Atomic
) == Atomic
)
948 char *name
= strdup((FileName
+ ".XXXXXX").c_str());
949 TemporaryFileName
= string(mktemp(name
));
952 else if ((Mode
& (Exclusive
| Create
)) == (Exclusive
| Create
))
954 // for atomic, this will be done by rename in Close()
955 unlink(FileName
.c_str());
957 if ((Mode
& Empty
) == Empty
)
960 if (lstat(FileName
.c_str(),&Buf
) == 0 && S_ISLNK(Buf
.st_mode
))
961 unlink(FileName
.c_str());
965 #define if_FLAGGED_SET(FLAG, MODE) if ((Mode & FLAG) == FLAG) fileflags |= MODE
966 if_FLAGGED_SET(ReadWrite
, O_RDWR
);
967 else if_FLAGGED_SET(ReadOnly
, O_RDONLY
);
968 else if_FLAGGED_SET(WriteOnly
, O_WRONLY
);
970 if_FLAGGED_SET(Create
, O_CREAT
);
971 if_FLAGGED_SET(Empty
, O_TRUNC
);
972 if_FLAGGED_SET(Exclusive
, O_EXCL
);
973 else if_FLAGGED_SET(Atomic
, O_EXCL
);
974 #undef if_FLAGGED_SET
976 if (TemporaryFileName
.empty() == false)
977 iFd
= open(TemporaryFileName
.c_str(), fileflags
, Perms
);
979 iFd
= open(FileName
.c_str(), fileflags
, Perms
);
981 this->FileName
= FileName
;
982 if (iFd
== -1 || OpenInternDescriptor(Mode
, compressor
) == false)
989 return FileFdErrno("open",_("Could not open file %s"), FileName
.c_str());
992 SetCloseExec(iFd
,true);
996 // FileFd::OpenDescriptor - Open a filedescriptor /*{{{*/
997 // ---------------------------------------------------------------------
999 bool FileFd::OpenDescriptor(int Fd
, unsigned int const Mode
, CompressMode Compress
, bool AutoClose
)
1001 std::vector
<APT::Configuration::Compressor
> const compressors
= APT::Configuration::getCompressors();
1002 std::vector
<APT::Configuration::Compressor
>::const_iterator compressor
= compressors
.begin();
1005 // compat with the old API
1006 if (Mode
== ReadOnlyGzip
&& Compress
== None
)
1011 case None
: name
= "."; break;
1012 case Gzip
: name
= "gzip"; break;
1013 case Bzip2
: name
= "bzip2"; break;
1014 case Lzma
: name
= "lzma"; break;
1015 case Xz
: name
= "xz"; break;
1018 if (AutoClose
== true && Fd
!= -1)
1020 return FileFdError("Opening Fd %d in Auto or Extension compression mode is not supported", Fd
);
1022 for (; compressor
!= compressors
.end(); ++compressor
)
1023 if (compressor
->Name
== name
)
1025 if (compressor
== compressors
.end())
1027 if (AutoClose
== true && Fd
!= -1)
1029 return FileFdError("Can't find a configured compressor %s for file %s", name
.c_str(), FileName
.c_str());
1031 return OpenDescriptor(Fd
, Mode
, *compressor
, AutoClose
);
1033 bool FileFd::OpenDescriptor(int Fd
, unsigned int const Mode
, APT::Configuration::Compressor
const &compressor
, bool AutoClose
)
1036 Flags
= (AutoClose
) ? FileFd::AutoClose
: 0;
1037 if (AutoClose
== false && (
1039 compressor
.Name
== "gzip" ||
1042 compressor
.Name
== "bzip2" ||
1046 // Need to duplicate fd here or gzclose for cleanup will close the fd as well
1051 this->FileName
= "";
1052 if (Fd
== -1 || OpenInternDescriptor(Mode
, compressor
) == false)
1056 compressor
.Name
== "gzip" ||
1059 compressor
.Name
== "bzip2" ||
1066 return FileFdError(_("Could not open file descriptor %d"), Fd
);
1070 bool FileFd::OpenInternDescriptor(unsigned int const Mode
, APT::Configuration::Compressor
const &compressor
)
1072 if (compressor
.Name
== "." || compressor
.Binary
.empty() == true)
1077 d
= new FileFdPrivate();
1079 d
->compressor
= compressor
;
1083 if (compressor
.Name
== "gzip")
1090 if ((Mode
& ReadWrite
) == ReadWrite
)
1091 d
->gz
= gzdopen(iFd
, "r+");
1092 else if ((Mode
& WriteOnly
) == WriteOnly
)
1093 d
->gz
= gzdopen(iFd
, "w");
1095 d
->gz
= gzdopen(iFd
, "r");
1098 Flags
|= Compressed
;
1103 if (compressor
.Name
== "bzip2")
1107 BZ2_bzclose(d
->bz2
);
1110 if ((Mode
& ReadWrite
) == ReadWrite
)
1111 d
->bz2
= BZ2_bzdopen(iFd
, "r+");
1112 else if ((Mode
& WriteOnly
) == WriteOnly
)
1113 d
->bz2
= BZ2_bzdopen(iFd
, "w");
1115 d
->bz2
= BZ2_bzdopen(iFd
, "r");
1118 Flags
|= Compressed
;
1123 // collect zombies here in case we reopen
1124 if (d
->compressor_pid
> 0)
1125 ExecWait(d
->compressor_pid
, "FileFdCompressor", true);
1127 if ((Mode
& ReadWrite
) == ReadWrite
)
1128 return FileFdError("ReadWrite mode is not supported for file %s", FileName
.c_str());
1130 bool const Comp
= (Mode
& WriteOnly
) == WriteOnly
;
1133 // Handle 'decompression' of empty files
1136 if (Buf
.st_size
== 0 && S_ISFIFO(Buf
.st_mode
) == false)
1139 // We don't need the file open - instead let the compressor open it
1140 // as he properly knows better how to efficiently read from 'his' file
1141 if (FileName
.empty() == false)
1148 // Create a data pipe
1149 int Pipe
[2] = {-1,-1};
1150 if (pipe(Pipe
) != 0)
1151 return FileFdErrno("pipe",_("Failed to create subprocess IPC"));
1152 for (int J
= 0; J
!= 2; J
++)
1153 SetCloseExec(Pipe
[J
],true);
1155 d
->compressed_fd
= iFd
;
1164 d
->compressor_pid
= ExecFork();
1165 if (d
->compressor_pid
== 0)
1169 dup2(d
->compressed_fd
,STDOUT_FILENO
);
1170 dup2(Pipe
[0],STDIN_FILENO
);
1174 if (FileName
.empty() == true)
1175 dup2(d
->compressed_fd
,STDIN_FILENO
);
1176 dup2(Pipe
[1],STDOUT_FILENO
);
1178 int const nullfd
= open("/dev/null", O_WRONLY
);
1181 dup2(nullfd
,STDERR_FILENO
);
1185 SetCloseExec(STDOUT_FILENO
,false);
1186 SetCloseExec(STDIN_FILENO
,false);
1188 std::vector
<char const*> Args
;
1189 Args
.push_back(compressor
.Binary
.c_str());
1190 std::vector
<std::string
> const * const addArgs
=
1191 (Comp
== true) ? &(compressor
.CompressArgs
) : &(compressor
.UncompressArgs
);
1192 for (std::vector
<std::string
>::const_iterator a
= addArgs
->begin();
1193 a
!= addArgs
->end(); ++a
)
1194 Args
.push_back(a
->c_str());
1195 if (Comp
== false && FileName
.empty() == false)
1197 Args
.push_back("--stdout");
1198 if (TemporaryFileName
.empty() == false)
1199 Args
.push_back(TemporaryFileName
.c_str());
1201 Args
.push_back(FileName
.c_str());
1203 Args
.push_back(NULL
);
1205 execvp(Args
[0],(char **)&Args
[0]);
1206 cerr
<< _("Failed to exec compressor ") << Args
[0] << endl
;
1217 // FileFd::~File - Closes the file /*{{{*/
1218 // ---------------------------------------------------------------------
1219 /* If the proper modes are selected then we close the Fd and possibly
1220 unlink the file on error. */
1225 d
->CloseDown(FileName
);
1230 // FileFd::Read - Read a bit of the file /*{{{*/
1231 // ---------------------------------------------------------------------
1232 /* We are carefull to handle interruption by a signal while reading
1234 bool FileFd::Read(void *To
,unsigned long long Size
,unsigned long long *Actual
)
1240 *((char *)To
) = '\0';
1244 if (d
!= NULL
&& d
->gz
!= NULL
)
1245 Res
= gzread(d
->gz
,To
,Size
);
1249 if (d
!= NULL
&& d
->bz2
!= NULL
)
1250 Res
= BZ2_bzread(d
->bz2
,To
,Size
);
1253 Res
= read(iFd
,To
,Size
);
1260 if (d
!= NULL
&& d
->gz
!= NULL
)
1263 char const * const errmsg
= gzerror(d
->gz
, &err
);
1265 return FileFdError("gzread: %s (%d: %s)", _("Read error"), err
, errmsg
);
1269 if (d
!= NULL
&& d
->bz2
!= NULL
)
1272 char const * const errmsg
= BZ2_bzerror(d
->bz2
, &err
);
1273 if (err
!= BZ_IO_ERROR
)
1274 return FileFdError("BZ2_bzread: %s (%d: %s)", _("Read error"), err
, errmsg
);
1277 return FileFdErrno("read",_("Read error"));
1280 To
= (char *)To
+ Res
;
1287 while (Res
> 0 && Size
> 0);
1299 return FileFdError(_("read, still have %llu to read but none left"), Size
);
1302 // FileFd::ReadLine - Read a complete line from the file /*{{{*/
1303 // ---------------------------------------------------------------------
1304 /* Beware: This method can be quiet slow for big buffers on UNcompressed
1305 files because of the naive implementation! */
1306 char* FileFd::ReadLine(char *To
, unsigned long long const Size
)
1310 if (d
!= NULL
&& d
->gz
!= NULL
)
1311 return gzgets(d
->gz
, To
, Size
);
1314 unsigned long long read
= 0;
1315 while ((Size
- 1) != read
)
1317 unsigned long long done
= 0;
1318 if (Read(To
+ read
, 1, &done
) == false)
1322 if (To
[read
++] == '\n')
1331 // FileFd::Write - Write to the file /*{{{*/
1332 // ---------------------------------------------------------------------
1334 bool FileFd::Write(const void *From
,unsigned long long Size
)
1341 if (d
!= NULL
&& d
->gz
!= NULL
)
1342 Res
= gzwrite(d
->gz
,From
,Size
);
1346 if (d
!= NULL
&& d
->bz2
!= NULL
)
1347 Res
= BZ2_bzwrite(d
->bz2
,(void*)From
,Size
);
1350 Res
= write(iFd
,From
,Size
);
1351 if (Res
< 0 && errno
== EINTR
)
1356 if (d
!= NULL
&& d
->gz
!= NULL
)
1359 char const * const errmsg
= gzerror(d
->gz
, &err
);
1361 return FileFdError("gzwrite: %s (%d: %s)", _("Write error"), err
, errmsg
);
1365 if (d
!= NULL
&& d
->bz2
!= NULL
)
1368 char const * const errmsg
= BZ2_bzerror(d
->bz2
, &err
);
1369 if (err
!= BZ_IO_ERROR
)
1370 return FileFdError("BZ2_bzwrite: %s (%d: %s)", _("Write error"), err
, errmsg
);
1373 return FileFdErrno("write",_("Write error"));
1376 From
= (char *)From
+ Res
;
1381 while (Res
> 0 && Size
> 0);
1386 return FileFdError(_("write, still have %llu to write but couldn't"), Size
);
1388 bool FileFd::Write(int Fd
, const void *From
, unsigned long long Size
)
1394 Res
= write(Fd
,From
,Size
);
1395 if (Res
< 0 && errno
== EINTR
)
1398 return _error
->Errno("write",_("Write error"));
1400 From
= (char *)From
+ Res
;
1403 while (Res
> 0 && Size
> 0);
1408 return _error
->Error(_("write, still have %llu to write but couldn't"), Size
);
1411 // FileFd::Seek - Seek in the file /*{{{*/
1412 // ---------------------------------------------------------------------
1414 bool FileFd::Seek(unsigned long long To
)
1416 if (d
!= NULL
&& (d
->pipe
== true
1422 // Our poor man seeking in pipes is costly, so try to avoid it
1423 unsigned long long seekpos
= Tell();
1426 else if (seekpos
< To
)
1427 return Skip(To
- seekpos
);
1429 if ((d
->openmode
& ReadOnly
) != ReadOnly
)
1430 return FileFdError("Reopen is only implemented for read-only files!");
1434 BZ2_bzclose(d
->bz2
);
1441 if (TemporaryFileName
.empty() == false)
1442 iFd
= open(TemporaryFileName
.c_str(), O_RDONLY
);
1443 else if (FileName
.empty() == false)
1444 iFd
= open(FileName
.c_str(), O_RDONLY
);
1447 if (d
->compressed_fd
> 0)
1448 if (lseek(d
->compressed_fd
, 0, SEEK_SET
) != 0)
1449 iFd
= d
->compressed_fd
;
1451 return FileFdError("Reopen is not implemented for pipes opened with FileFd::OpenDescriptor()!");
1454 if (OpenInternDescriptor(d
->openmode
, d
->compressor
) == false)
1455 return FileFdError("Seek on file %s because it couldn't be reopened", FileName
.c_str());
1465 if (d
!= NULL
&& d
->gz
)
1466 res
= gzseek(d
->gz
,To
,SEEK_SET
);
1469 res
= lseek(iFd
,To
,SEEK_SET
);
1470 if (res
!= (signed)To
)
1471 return FileFdError("Unable to seek to %llu", To
);
1478 // FileFd::Skip - Seek in the file /*{{{*/
1479 // ---------------------------------------------------------------------
1481 bool FileFd::Skip(unsigned long long Over
)
1483 if (d
!= NULL
&& (d
->pipe
== true
1493 unsigned long long toread
= std::min((unsigned long long) sizeof(buffer
), Over
);
1494 if (Read(buffer
, toread
) == false)
1495 return FileFdError("Unable to seek ahead %llu",Over
);
1503 if (d
!= NULL
&& d
->gz
!= NULL
)
1504 res
= gzseek(d
->gz
,Over
,SEEK_CUR
);
1507 res
= lseek(iFd
,Over
,SEEK_CUR
);
1509 return FileFdError("Unable to seek ahead %llu",Over
);
1516 // FileFd::Truncate - Truncate the file /*{{{*/
1517 // ---------------------------------------------------------------------
1519 bool FileFd::Truncate(unsigned long long To
)
1521 #if defined HAVE_ZLIB || defined HAVE_BZ2
1522 if (d
!= NULL
&& (d
->gz
!= NULL
|| d
->bz2
!= NULL
))
1523 return FileFdError("Truncating compressed files is not implemented (%s)", FileName
.c_str());
1525 if (ftruncate(iFd
,To
) != 0)
1526 return FileFdError("Unable to truncate to %llu",To
);
1531 // FileFd::Tell - Current seek position /*{{{*/
1532 // ---------------------------------------------------------------------
1534 unsigned long long FileFd::Tell()
1536 // In theory, we could just return seekpos here always instead of
1537 // seeking around, but not all users of FileFd use always Seek() and co
1538 // so d->seekpos isn't always true and we can just use it as a hint if
1539 // we have nothing else, but not always as an authority…
1540 if (d
!= NULL
&& (d
->pipe
== true
1549 if (d
!= NULL
&& d
->gz
!= NULL
)
1550 Res
= gztell(d
->gz
);
1553 Res
= lseek(iFd
,0,SEEK_CUR
);
1554 if (Res
== (off_t
)-1)
1555 FileFdErrno("lseek","Failed to determine the current file position");
1561 // FileFd::FileSize - Return the size of the file /*{{{*/
1562 // ---------------------------------------------------------------------
1564 unsigned long long FileFd::FileSize()
1567 if ((d
== NULL
|| d
->pipe
== false) && fstat(iFd
,&Buf
) != 0)
1568 return FileFdErrno("fstat","Unable to determine the file size");
1570 // for compressor pipes st_size is undefined and at 'best' zero
1571 if ((d
!= NULL
&& d
->pipe
== true) || S_ISFIFO(Buf
.st_mode
))
1573 // we set it here, too, as we get the info here for free
1574 // in theory the Open-methods should take care of it already
1577 if (stat(FileName
.c_str(), &Buf
) != 0)
1578 return FileFdErrno("stat","Unable to determine the file size");
1584 // FileFd::Size - Return the size of the content in the file /*{{{*/
1585 // ---------------------------------------------------------------------
1587 unsigned long long FileFd::Size()
1589 unsigned long long size
= FileSize();
1591 // for compressor pipes st_size is undefined and at 'best' zero,
1592 // so we 'read' the content and 'seek' back - see there
1593 if (d
!= NULL
&& (d
->pipe
== true
1595 || (d
->bz2
&& size
> 0)
1599 unsigned long long const oldSeek
= Tell();
1601 unsigned long long read
= 0;
1603 Read(ignore
, sizeof(ignore
), &read
);
1609 // only check gzsize if we are actually a gzip file, just checking for
1610 // "gz" is not sufficient as uncompressed files could be opened with
1611 // gzopen in "direct" mode as well
1612 else if (d
!= NULL
&& d
->gz
&& !gzdirect(d
->gz
) && size
> 0)
1614 off_t
const oldPos
= lseek(iFd
,0,SEEK_CUR
);
1615 /* unfortunately zlib.h doesn't provide a gzsize(), so we have to do
1616 * this ourselves; the original (uncompressed) file size is the last 32
1617 * bits of the file */
1618 // FIXME: Size for gz-files is limited by 32bit… no largefile support
1619 if (lseek(iFd
, -4, SEEK_END
) < 0)
1620 return FileFdErrno("lseek","Unable to seek to end of gzipped file");
1622 if (read(iFd
, &size
, 4) != 4)
1623 return FileFdErrno("read","Unable to read original size of gzipped file");
1625 #ifdef WORDS_BIGENDIAN
1626 uint32_t tmp_size
= size
;
1627 uint8_t const * const p
= (uint8_t const * const) &tmp_size
;
1628 tmp_size
= (p
[3] << 24) | (p
[2] << 16) | (p
[1] << 8) | p
[0];
1632 if (lseek(iFd
, oldPos
, SEEK_SET
) < 0)
1633 return FileFdErrno("lseek","Unable to seek in gzipped file");
1642 // FileFd::ModificationTime - Return the time of last touch /*{{{*/
1643 // ---------------------------------------------------------------------
1645 time_t FileFd::ModificationTime()
1648 if ((d
== NULL
|| d
->pipe
== false) && fstat(iFd
,&Buf
) != 0)
1650 FileFdErrno("fstat","Unable to determine the modification time of file %s", FileName
.c_str());
1654 // for compressor pipes st_size is undefined and at 'best' zero
1655 if ((d
!= NULL
&& d
->pipe
== true) || S_ISFIFO(Buf
.st_mode
))
1657 // we set it here, too, as we get the info here for free
1658 // in theory the Open-methods should take care of it already
1661 if (stat(FileName
.c_str(), &Buf
) != 0)
1663 FileFdErrno("fstat","Unable to determine the modification time of file %s", FileName
.c_str());
1668 return Buf
.st_mtime
;
1671 // FileFd::Close - Close the file if the close flag is set /*{{{*/
1672 // ---------------------------------------------------------------------
1674 bool FileFd::Close()
1680 if ((Flags
& AutoClose
) == AutoClose
)
1682 if ((Flags
& Compressed
) != Compressed
&& iFd
> 0 && close(iFd
) != 0)
1683 Res
&= _error
->Errno("close",_("Problem closing the file %s"), FileName
.c_str());
1687 Res
&= d
->CloseDown(FileName
);
1693 if ((Flags
& Replace
) == Replace
) {
1694 if (rename(TemporaryFileName
.c_str(), FileName
.c_str()) != 0)
1695 Res
&= _error
->Errno("rename",_("Problem renaming the file %s to %s"), TemporaryFileName
.c_str(), FileName
.c_str());
1697 FileName
= TemporaryFileName
; // for the unlink() below.
1698 TemporaryFileName
.clear();
1703 if ((Flags
& Fail
) == Fail
&& (Flags
& DelOnFail
) == DelOnFail
&&
1704 FileName
.empty() == false)
1705 if (unlink(FileName
.c_str()) != 0)
1706 Res
&= _error
->WarningE("unlnk",_("Problem unlinking the file %s"), FileName
.c_str());
1713 // FileFd::Sync - Sync the file /*{{{*/
1714 // ---------------------------------------------------------------------
1718 if (fsync(iFd
) != 0)
1719 return FileFdErrno("sync",_("Problem syncing the file"));
1723 // FileFd::FileFdErrno - set Fail and call _error->Errno *{{{*/
1724 bool FileFd::FileFdErrno(const char *Function
, const char *Description
,...)
1728 size_t msgSize
= 400;
1729 int const errsv
= errno
;
1732 va_start(args
,Description
);
1733 if (_error
->InsertErrno(GlobalError::ERROR
, Function
, Description
, args
, errsv
, msgSize
) == false)
1740 // FileFd::FileFdError - set Fail and call _error->Error *{{{*/
1741 bool FileFd::FileFdError(const char *Description
,...) {
1744 size_t msgSize
= 400;
1747 va_start(args
,Description
);
1748 if (_error
->Insert(GlobalError::ERROR
, Description
, args
, msgSize
) == false)
1756 gzFile
FileFd::gzFd() { return (gzFile
) d
->gz
; }