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
254 _error
->Warning(_("Not using locking for nfs mounted lock file %s"),File
.c_str());
255 return dup(0); // Need something for the caller to close
259 _error
->Errno("open",_("Could not get lock %s"),File
.c_str());
267 // FileExists - Check if a file exists /*{{{*/
268 // ---------------------------------------------------------------------
269 /* Beware: Directories are also files! */
270 bool FileExists(string File
)
273 if (stat(File
.c_str(),&Buf
) != 0)
278 // RealFileExists - Check if a file exists and if it is really a file /*{{{*/
279 // ---------------------------------------------------------------------
281 bool RealFileExists(string File
)
284 if (stat(File
.c_str(),&Buf
) != 0)
286 return ((Buf
.st_mode
& S_IFREG
) != 0);
289 // DirectoryExists - Check if a directory exists and is really one /*{{{*/
290 // ---------------------------------------------------------------------
292 bool DirectoryExists(string
const &Path
)
295 if (stat(Path
.c_str(),&Buf
) != 0)
297 return ((Buf
.st_mode
& S_IFDIR
) != 0);
300 // CreateDirectory - poor man's mkdir -p guarded by a parent directory /*{{{*/
301 // ---------------------------------------------------------------------
302 /* This method will create all directories needed for path in good old
303 mkdir -p style but refuses to do this if Parent is not a prefix of
304 this Path. Example: /var/cache/ and /var/cache/apt/archives are given,
305 so it will create apt/archives if /var/cache exists - on the other
306 hand if the parent is /var/lib the creation will fail as this path
307 is not a parent of the path to be generated. */
308 bool CreateDirectory(string
const &Parent
, string
const &Path
)
310 if (Parent
.empty() == true || Path
.empty() == true)
313 if (DirectoryExists(Path
) == true)
316 if (DirectoryExists(Parent
) == false)
319 // we are not going to create directories "into the blue"
320 if (Path
.find(Parent
, 0) != 0)
323 vector
<string
> const dirs
= VectorizeString(Path
.substr(Parent
.size()), '/');
324 string progress
= Parent
;
325 for (vector
<string
>::const_iterator d
= dirs
.begin(); d
!= dirs
.end(); ++d
)
327 if (d
->empty() == true)
330 progress
.append("/").append(*d
);
331 if (DirectoryExists(progress
) == true)
334 if (mkdir(progress
.c_str(), 0755) != 0)
340 // CreateAPTDirectoryIfNeeded - ensure that the given directory exists /*{{{*/
341 // ---------------------------------------------------------------------
342 /* a small wrapper around CreateDirectory to check if it exists and to
343 remove the trailing "/apt/" from the parent directory if needed */
344 bool CreateAPTDirectoryIfNeeded(string
const &Parent
, string
const &Path
)
346 if (DirectoryExists(Path
) == true)
349 size_t const len
= Parent
.size();
350 if (len
> 5 && Parent
.find("/apt/", len
- 6, 5) == len
- 5)
352 if (CreateDirectory(Parent
.substr(0,len
-5), Path
) == true)
355 else if (CreateDirectory(Parent
, Path
) == true)
361 // GetListOfFilesInDir - returns a vector of files in the given dir /*{{{*/
362 // ---------------------------------------------------------------------
363 /* If an extension is given only files with this extension are included
364 in the returned vector, otherwise every "normal" file is included. */
365 std::vector
<string
> GetListOfFilesInDir(string
const &Dir
, string
const &Ext
,
366 bool const &SortList
, bool const &AllowNoExt
)
368 std::vector
<string
> ext
;
370 if (Ext
.empty() == false)
372 if (AllowNoExt
== true && ext
.empty() == false)
374 return GetListOfFilesInDir(Dir
, ext
, SortList
);
376 std::vector
<string
> GetListOfFilesInDir(string
const &Dir
, std::vector
<string
> const &Ext
,
377 bool const &SortList
)
379 // Attention debuggers: need to be set with the environment config file!
380 bool const Debug
= _config
->FindB("Debug::GetListOfFilesInDir", false);
383 std::clog
<< "Accept in " << Dir
<< " only files with the following " << Ext
.size() << " extensions:" << std::endl
;
384 if (Ext
.empty() == true)
385 std::clog
<< "\tNO extension" << std::endl
;
387 for (std::vector
<string
>::const_iterator e
= Ext
.begin();
389 std::clog
<< '\t' << (e
->empty() == true ? "NO" : *e
) << " extension" << std::endl
;
392 std::vector
<string
> List
;
394 if (DirectoryExists(Dir
) == false)
396 _error
->Error(_("List of files can't be created as '%s' is not a directory"), Dir
.c_str());
400 Configuration::MatchAgainstConfig
SilentIgnore("Dir::Ignore-Files-Silently");
401 DIR *D
= opendir(Dir
.c_str());
404 _error
->Errno("opendir",_("Unable to read %s"),Dir
.c_str());
408 for (struct dirent
*Ent
= readdir(D
); Ent
!= 0; Ent
= readdir(D
))
410 // skip "hidden" files
411 if (Ent
->d_name
[0] == '.')
414 // Make sure it is a file and not something else
415 string
const File
= flCombine(Dir
,Ent
->d_name
);
416 #ifdef _DIRENT_HAVE_D_TYPE
417 if (Ent
->d_type
!= DT_REG
)
420 if (RealFileExists(File
) == false)
422 // do not show ignoration warnings for directories
424 #ifdef _DIRENT_HAVE_D_TYPE
425 Ent
->d_type
== DT_DIR
||
427 DirectoryExists(File
) == true)
429 if (SilentIgnore
.Match(Ent
->d_name
) == false)
430 _error
->Notice(_("Ignoring '%s' in directory '%s' as it is not a regular file"), Ent
->d_name
, Dir
.c_str());
435 // check for accepted extension:
436 // no extension given -> periods are bad as hell!
437 // extensions given -> "" extension allows no extension
438 if (Ext
.empty() == false)
440 string d_ext
= flExtension(Ent
->d_name
);
441 if (d_ext
== Ent
->d_name
) // no extension
443 if (std::find(Ext
.begin(), Ext
.end(), "") == Ext
.end())
446 std::clog
<< "Bad file: " << Ent
->d_name
<< " → no extension" << std::endl
;
447 if (SilentIgnore
.Match(Ent
->d_name
) == false)
448 _error
->Notice(_("Ignoring file '%s' in directory '%s' as it has no filename extension"), Ent
->d_name
, Dir
.c_str());
452 else if (std::find(Ext
.begin(), Ext
.end(), d_ext
) == Ext
.end())
455 std::clog
<< "Bad file: " << Ent
->d_name
<< " → bad extension »" << flExtension(Ent
->d_name
) << "«" << std::endl
;
456 if (SilentIgnore
.Match(Ent
->d_name
) == false)
457 _error
->Notice(_("Ignoring file '%s' in directory '%s' as it has an invalid filename extension"), Ent
->d_name
, Dir
.c_str());
462 // Skip bad filenames ala run-parts
463 const char *C
= Ent
->d_name
;
465 if (isalpha(*C
) == 0 && isdigit(*C
) == 0
466 && *C
!= '_' && *C
!= '-') {
467 // no required extension -> dot is a bad character
468 if (*C
== '.' && Ext
.empty() == false)
473 // we don't reach the end of the name -> bad character included
477 std::clog
<< "Bad file: " << Ent
->d_name
<< " → bad character »"
478 << *C
<< "« in filename (period allowed: " << (Ext
.empty() ? "no" : "yes") << ")" << std::endl
;
482 // skip filenames which end with a period. These are never valid
486 std::clog
<< "Bad file: " << Ent
->d_name
<< " → Period as last character" << std::endl
;
491 std::clog
<< "Accept file: " << Ent
->d_name
<< " in " << Dir
<< std::endl
;
492 List
.push_back(File
);
496 if (SortList
== true)
497 std::sort(List
.begin(),List
.end());
500 std::vector
<string
> GetListOfFilesInDir(string
const &Dir
, bool SortList
)
502 bool const Debug
= _config
->FindB("Debug::GetListOfFilesInDir", false);
504 std::clog
<< "Accept in " << Dir
<< " all regular files" << std::endl
;
506 std::vector
<string
> List
;
508 if (DirectoryExists(Dir
) == false)
510 _error
->Error(_("List of files can't be created as '%s' is not a directory"), Dir
.c_str());
514 DIR *D
= opendir(Dir
.c_str());
517 _error
->Errno("opendir",_("Unable to read %s"),Dir
.c_str());
521 for (struct dirent
*Ent
= readdir(D
); Ent
!= 0; Ent
= readdir(D
))
523 // skip "hidden" files
524 if (Ent
->d_name
[0] == '.')
527 // Make sure it is a file and not something else
528 string
const File
= flCombine(Dir
,Ent
->d_name
);
529 #ifdef _DIRENT_HAVE_D_TYPE
530 if (Ent
->d_type
!= DT_REG
)
533 if (RealFileExists(File
) == false)
536 std::clog
<< "Bad file: " << Ent
->d_name
<< " → it is not a real file" << std::endl
;
541 // Skip bad filenames ala run-parts
542 const char *C
= Ent
->d_name
;
544 if (isalpha(*C
) == 0 && isdigit(*C
) == 0
545 && *C
!= '_' && *C
!= '-' && *C
!= '.')
548 // we don't reach the end of the name -> bad character included
552 std::clog
<< "Bad file: " << Ent
->d_name
<< " → bad character »" << *C
<< "« in filename" << std::endl
;
556 // skip filenames which end with a period. These are never valid
560 std::clog
<< "Bad file: " << Ent
->d_name
<< " → Period as last character" << std::endl
;
565 std::clog
<< "Accept file: " << Ent
->d_name
<< " in " << Dir
<< std::endl
;
566 List
.push_back(File
);
570 if (SortList
== true)
571 std::sort(List
.begin(),List
.end());
575 // SafeGetCWD - This is a safer getcwd that returns a dynamic string /*{{{*/
576 // ---------------------------------------------------------------------
577 /* We return / on failure. */
580 // Stash the current dir.
583 if (getcwd(S
,sizeof(S
)-2) == 0)
585 unsigned int Len
= strlen(S
);
591 // GetModificationTime - Get the mtime of the given file or -1 on error /*{{{*/
592 // ---------------------------------------------------------------------
593 /* We return / on failure. */
594 time_t GetModificationTime(string
const &Path
)
597 if (stat(Path
.c_str(), &St
) < 0)
602 // flNotDir - Strip the directory from the filename /*{{{*/
603 // ---------------------------------------------------------------------
605 string
flNotDir(string File
)
607 string::size_type Res
= File
.rfind('/');
608 if (Res
== string::npos
)
611 return string(File
,Res
,Res
- File
.length());
614 // flNotFile - Strip the file from the directory name /*{{{*/
615 // ---------------------------------------------------------------------
616 /* Result ends in a / */
617 string
flNotFile(string File
)
619 string::size_type Res
= File
.rfind('/');
620 if (Res
== string::npos
)
623 return string(File
,0,Res
);
626 // flExtension - Return the extension for the file /*{{{*/
627 // ---------------------------------------------------------------------
629 string
flExtension(string File
)
631 string::size_type Res
= File
.rfind('.');
632 if (Res
== string::npos
)
635 return string(File
,Res
,Res
- File
.length());
638 // flNoLink - If file is a symlink then deref it /*{{{*/
639 // ---------------------------------------------------------------------
640 /* If the name is not a link then the returned path is the input. */
641 string
flNoLink(string File
)
644 if (lstat(File
.c_str(),&St
) != 0 || S_ISLNK(St
.st_mode
) == 0)
646 if (stat(File
.c_str(),&St
) != 0)
649 /* Loop resolving the link. There is no need to limit the number of
650 loops because the stat call above ensures that the symlink is not
658 if ((Res
= readlink(NFile
.c_str(),Buffer
,sizeof(Buffer
))) <= 0 ||
659 (unsigned)Res
>= sizeof(Buffer
))
662 // Append or replace the previous path
664 if (Buffer
[0] == '/')
667 NFile
= flNotFile(NFile
) + Buffer
;
669 // See if we are done
670 if (lstat(NFile
.c_str(),&St
) != 0)
672 if (S_ISLNK(St
.st_mode
) == 0)
677 // flCombine - Combine a file and a directory /*{{{*/
678 // ---------------------------------------------------------------------
679 /* If the file is an absolute path then it is just returned, otherwise
680 the directory is pre-pended to it. */
681 string
flCombine(string Dir
,string File
)
683 if (File
.empty() == true)
686 if (File
[0] == '/' || Dir
.empty() == true)
688 if (File
.length() >= 2 && File
[0] == '.' && File
[1] == '/')
690 if (Dir
[Dir
.length()-1] == '/')
692 return Dir
+ '/' + File
;
695 // SetCloseExec - Set the close on exec flag /*{{{*/
696 // ---------------------------------------------------------------------
698 void SetCloseExec(int Fd
,bool Close
)
700 if (fcntl(Fd
,F_SETFD
,(Close
== false)?0:FD_CLOEXEC
) != 0)
702 cerr
<< "FATAL -> Could not set close on exec " << strerror(errno
) << endl
;
707 // SetNonBlock - Set the nonblocking flag /*{{{*/
708 // ---------------------------------------------------------------------
710 void SetNonBlock(int Fd
,bool Block
)
712 int Flags
= fcntl(Fd
,F_GETFL
) & (~O_NONBLOCK
);
713 if (fcntl(Fd
,F_SETFL
,Flags
| ((Block
== false)?0:O_NONBLOCK
)) != 0)
715 cerr
<< "FATAL -> Could not set non-blocking flag " << strerror(errno
) << endl
;
720 // WaitFd - Wait for a FD to become readable /*{{{*/
721 // ---------------------------------------------------------------------
722 /* This waits for a FD to become readable using select. It is useful for
723 applications making use of non-blocking sockets. The timeout is
725 bool WaitFd(int Fd
,bool write
,unsigned long timeout
)
738 Res
= select(Fd
+1,0,&Set
,0,(timeout
!= 0?&tv
:0));
740 while (Res
< 0 && errno
== EINTR
);
750 Res
= select(Fd
+1,&Set
,0,0,(timeout
!= 0?&tv
:0));
752 while (Res
< 0 && errno
== EINTR
);
761 // ExecFork - Magical fork that sanitizes the context before execing /*{{{*/
762 // ---------------------------------------------------------------------
763 /* This is used if you want to cleanse the environment for the forked
764 child, it fixes up the important signals and nukes all of the fds,
765 otherwise acts like normal fork. */
768 // Fork off the process
769 pid_t Process
= fork();
772 cerr
<< "FATAL -> Failed to fork." << endl
;
776 // Spawn the subprocess
780 signal(SIGPIPE
,SIG_DFL
);
781 signal(SIGQUIT
,SIG_DFL
);
782 signal(SIGINT
,SIG_DFL
);
783 signal(SIGWINCH
,SIG_DFL
);
784 signal(SIGCONT
,SIG_DFL
);
785 signal(SIGTSTP
,SIG_DFL
);
788 Configuration::Item
const *Opts
= _config
->Tree("APT::Keep-Fds");
789 if (Opts
!= 0 && Opts
->Child
!= 0)
792 for (; Opts
!= 0; Opts
= Opts
->Next
)
794 if (Opts
->Value
.empty() == true)
796 int fd
= atoi(Opts
->Value
.c_str());
801 // Close all of our FDs - just in case
802 for (int K
= 3; K
!= 40; K
++)
804 if(KeepFDs
.find(K
) == KeepFDs
.end())
805 fcntl(K
,F_SETFD
,FD_CLOEXEC
);
812 // ExecWait - Fancy waitpid /*{{{*/
813 // ---------------------------------------------------------------------
814 /* Waits for the given sub process. If Reap is set then no errors are
815 generated. Otherwise a failed subprocess will generate a proper descriptive
817 bool ExecWait(pid_t Pid
,const char *Name
,bool Reap
)
822 // Wait and collect the error code
824 while (waitpid(Pid
,&Status
,0) != Pid
)
832 return _error
->Error(_("Waited for %s but it wasn't there"),Name
);
836 // Check for an error code.
837 if (WIFEXITED(Status
) == 0 || WEXITSTATUS(Status
) != 0)
841 if (WIFSIGNALED(Status
) != 0)
843 if( WTERMSIG(Status
) == SIGSEGV
)
844 return _error
->Error(_("Sub-process %s received a segmentation fault."),Name
);
846 return _error
->Error(_("Sub-process %s received signal %u."),Name
, WTERMSIG(Status
));
849 if (WIFEXITED(Status
) != 0)
850 return _error
->Error(_("Sub-process %s returned an error code (%u)"),Name
,WEXITSTATUS(Status
));
852 return _error
->Error(_("Sub-process %s exited unexpectedly"),Name
);
859 // FileFd::Open - Open a file /*{{{*/
860 // ---------------------------------------------------------------------
861 /* The most commonly used open mode combinations are given with Mode */
862 bool FileFd::Open(string FileName
,unsigned int const Mode
,CompressMode Compress
, unsigned long const Perms
)
864 if (Mode
== ReadOnlyGzip
)
865 return Open(FileName
, ReadOnly
, Gzip
, Perms
);
867 if (Compress
== Auto
&& (Mode
& WriteOnly
) == WriteOnly
)
868 return FileFdError("Autodetection on %s only works in ReadOnly openmode!", FileName
.c_str());
870 std::vector
<APT::Configuration::Compressor
> const compressors
= APT::Configuration::getCompressors();
871 std::vector
<APT::Configuration::Compressor
>::const_iterator compressor
= compressors
.begin();
872 if (Compress
== Auto
)
874 for (; compressor
!= compressors
.end(); ++compressor
)
876 std::string file
= std::string(FileName
).append(compressor
->Extension
);
877 if (FileExists(file
) == false)
883 else if (Compress
== Extension
)
885 std::string::size_type
const found
= FileName
.find_last_of('.');
887 if (found
!= std::string::npos
)
889 ext
= FileName
.substr(found
);
890 if (ext
== ".new" || ext
== ".bak")
892 std::string::size_type
const found2
= FileName
.find_last_of('.', found
- 1);
893 if (found2
!= std::string::npos
)
894 ext
= FileName
.substr(found2
, found
- found2
);
899 for (; compressor
!= compressors
.end(); ++compressor
)
900 if (ext
== compressor
->Extension
)
902 // no matching extension - assume uncompressed (imagine files like 'example.org_Packages')
903 if (compressor
== compressors
.end())
904 for (compressor
= compressors
.begin(); compressor
!= compressors
.end(); ++compressor
)
905 if (compressor
->Name
== ".")
913 case None
: name
= "."; break;
914 case Gzip
: name
= "gzip"; break;
915 case Bzip2
: name
= "bzip2"; break;
916 case Lzma
: name
= "lzma"; break;
917 case Xz
: name
= "xz"; break;
921 return FileFdError("Opening File %s in None, Auto or Extension should be already handled?!?", FileName
.c_str());
923 for (; compressor
!= compressors
.end(); ++compressor
)
924 if (compressor
->Name
== name
)
926 if (compressor
== compressors
.end())
927 return FileFdError("Can't find a configured compressor %s for file %s", name
.c_str(), FileName
.c_str());
930 if (compressor
== compressors
.end())
931 return FileFdError("Can't find a match for specified compressor mode for file %s", FileName
.c_str());
932 return Open(FileName
, Mode
, *compressor
, Perms
);
934 bool FileFd::Open(string FileName
,unsigned int const Mode
,APT::Configuration::Compressor
const &compressor
, unsigned long const Perms
)
939 if ((Mode
& WriteOnly
) != WriteOnly
&& (Mode
& (Atomic
| Create
| Empty
| Exclusive
)) != 0)
940 return FileFdError("ReadOnly mode for %s doesn't accept additional flags!", FileName
.c_str());
941 if ((Mode
& ReadWrite
) == 0)
942 return FileFdError("No openmode provided in FileFd::Open for %s", FileName
.c_str());
944 if ((Mode
& Atomic
) == Atomic
)
947 char *name
= strdup((FileName
+ ".XXXXXX").c_str());
948 TemporaryFileName
= string(mktemp(name
));
951 else if ((Mode
& (Exclusive
| Create
)) == (Exclusive
| Create
))
953 // for atomic, this will be done by rename in Close()
954 unlink(FileName
.c_str());
956 if ((Mode
& Empty
) == Empty
)
959 if (lstat(FileName
.c_str(),&Buf
) == 0 && S_ISLNK(Buf
.st_mode
))
960 unlink(FileName
.c_str());
964 #define if_FLAGGED_SET(FLAG, MODE) if ((Mode & FLAG) == FLAG) fileflags |= MODE
965 if_FLAGGED_SET(ReadWrite
, O_RDWR
);
966 else if_FLAGGED_SET(ReadOnly
, O_RDONLY
);
967 else if_FLAGGED_SET(WriteOnly
, O_WRONLY
);
969 if_FLAGGED_SET(Create
, O_CREAT
);
970 if_FLAGGED_SET(Empty
, O_TRUNC
);
971 if_FLAGGED_SET(Exclusive
, O_EXCL
);
972 else if_FLAGGED_SET(Atomic
, O_EXCL
);
973 #undef if_FLAGGED_SET
975 if (TemporaryFileName
.empty() == false)
976 iFd
= open(TemporaryFileName
.c_str(), fileflags
, Perms
);
978 iFd
= open(FileName
.c_str(), fileflags
, Perms
);
980 this->FileName
= FileName
;
981 if (iFd
== -1 || OpenInternDescriptor(Mode
, compressor
) == false)
988 return FileFdErrno("open",_("Could not open file %s"), FileName
.c_str());
991 SetCloseExec(iFd
,true);
995 // FileFd::OpenDescriptor - Open a filedescriptor /*{{{*/
996 // ---------------------------------------------------------------------
998 bool FileFd::OpenDescriptor(int Fd
, unsigned int const Mode
, CompressMode Compress
, bool AutoClose
)
1000 std::vector
<APT::Configuration::Compressor
> const compressors
= APT::Configuration::getCompressors();
1001 std::vector
<APT::Configuration::Compressor
>::const_iterator compressor
= compressors
.begin();
1004 // compat with the old API
1005 if (Mode
== ReadOnlyGzip
&& Compress
== None
)
1010 case None
: name
= "."; break;
1011 case Gzip
: name
= "gzip"; break;
1012 case Bzip2
: name
= "bzip2"; break;
1013 case Lzma
: name
= "lzma"; break;
1014 case Xz
: name
= "xz"; break;
1017 if (AutoClose
== true && Fd
!= -1)
1019 return FileFdError("Opening Fd %d in Auto or Extension compression mode is not supported", Fd
);
1021 for (; compressor
!= compressors
.end(); ++compressor
)
1022 if (compressor
->Name
== name
)
1024 if (compressor
== compressors
.end())
1026 if (AutoClose
== true && Fd
!= -1)
1028 return FileFdError("Can't find a configured compressor %s for file %s", name
.c_str(), FileName
.c_str());
1030 return OpenDescriptor(Fd
, Mode
, *compressor
, AutoClose
);
1032 bool FileFd::OpenDescriptor(int Fd
, unsigned int const Mode
, APT::Configuration::Compressor
const &compressor
, bool AutoClose
)
1035 Flags
= (AutoClose
) ? FileFd::AutoClose
: 0;
1036 if (AutoClose
== false && (
1038 compressor
.Name
== "gzip" ||
1041 compressor
.Name
== "bzip2" ||
1045 // Need to duplicate fd here or gzclose for cleanup will close the fd as well
1050 this->FileName
= "";
1051 if (Fd
== -1 || OpenInternDescriptor(Mode
, compressor
) == false)
1055 compressor
.Name
== "gzip" ||
1058 compressor
.Name
== "bzip2" ||
1065 return FileFdError(_("Could not open file descriptor %d"), Fd
);
1069 bool FileFd::OpenInternDescriptor(unsigned int const Mode
, APT::Configuration::Compressor
const &compressor
)
1071 if (compressor
.Name
== "." || compressor
.Binary
.empty() == true)
1076 d
= new FileFdPrivate();
1078 d
->compressor
= compressor
;
1082 if (compressor
.Name
== "gzip")
1089 if ((Mode
& ReadWrite
) == ReadWrite
)
1090 d
->gz
= gzdopen(iFd
, "r+");
1091 else if ((Mode
& WriteOnly
) == WriteOnly
)
1092 d
->gz
= gzdopen(iFd
, "w");
1094 d
->gz
= gzdopen(iFd
, "r");
1097 Flags
|= Compressed
;
1102 if (compressor
.Name
== "bzip2")
1106 BZ2_bzclose(d
->bz2
);
1109 if ((Mode
& ReadWrite
) == ReadWrite
)
1110 d
->bz2
= BZ2_bzdopen(iFd
, "r+");
1111 else if ((Mode
& WriteOnly
) == WriteOnly
)
1112 d
->bz2
= BZ2_bzdopen(iFd
, "w");
1114 d
->bz2
= BZ2_bzdopen(iFd
, "r");
1117 Flags
|= Compressed
;
1122 // collect zombies here in case we reopen
1123 if (d
->compressor_pid
> 0)
1124 ExecWait(d
->compressor_pid
, "FileFdCompressor", true);
1126 if ((Mode
& ReadWrite
) == ReadWrite
)
1127 return FileFdError("ReadWrite mode is not supported for file %s", FileName
.c_str());
1129 bool const Comp
= (Mode
& WriteOnly
) == WriteOnly
;
1132 // Handle 'decompression' of empty files
1135 if (Buf
.st_size
== 0 && S_ISFIFO(Buf
.st_mode
) == false)
1138 // We don't need the file open - instead let the compressor open it
1139 // as he properly knows better how to efficiently read from 'his' file
1140 if (FileName
.empty() == false)
1147 // Create a data pipe
1148 int Pipe
[2] = {-1,-1};
1149 if (pipe(Pipe
) != 0)
1150 return FileFdErrno("pipe",_("Failed to create subprocess IPC"));
1151 for (int J
= 0; J
!= 2; J
++)
1152 SetCloseExec(Pipe
[J
],true);
1154 d
->compressed_fd
= iFd
;
1163 d
->compressor_pid
= ExecFork();
1164 if (d
->compressor_pid
== 0)
1168 dup2(d
->compressed_fd
,STDOUT_FILENO
);
1169 dup2(Pipe
[0],STDIN_FILENO
);
1173 if (FileName
.empty() == true)
1174 dup2(d
->compressed_fd
,STDIN_FILENO
);
1175 dup2(Pipe
[1],STDOUT_FILENO
);
1177 int const nullfd
= open("/dev/null", O_WRONLY
);
1180 dup2(nullfd
,STDERR_FILENO
);
1184 SetCloseExec(STDOUT_FILENO
,false);
1185 SetCloseExec(STDIN_FILENO
,false);
1187 std::vector
<char const*> Args
;
1188 Args
.push_back(compressor
.Binary
.c_str());
1189 std::vector
<std::string
> const * const addArgs
=
1190 (Comp
== true) ? &(compressor
.CompressArgs
) : &(compressor
.UncompressArgs
);
1191 for (std::vector
<std::string
>::const_iterator a
= addArgs
->begin();
1192 a
!= addArgs
->end(); ++a
)
1193 Args
.push_back(a
->c_str());
1194 if (Comp
== false && FileName
.empty() == false)
1196 Args
.push_back("--stdout");
1197 if (TemporaryFileName
.empty() == false)
1198 Args
.push_back(TemporaryFileName
.c_str());
1200 Args
.push_back(FileName
.c_str());
1202 Args
.push_back(NULL
);
1204 execvp(Args
[0],(char **)&Args
[0]);
1205 cerr
<< _("Failed to exec compressor ") << Args
[0] << endl
;
1216 // FileFd::~File - Closes the file /*{{{*/
1217 // ---------------------------------------------------------------------
1218 /* If the proper modes are selected then we close the Fd and possibly
1219 unlink the file on error. */
1224 d
->CloseDown(FileName
);
1229 // FileFd::Read - Read a bit of the file /*{{{*/
1230 // ---------------------------------------------------------------------
1231 /* We are carefull to handle interruption by a signal while reading
1233 bool FileFd::Read(void *To
,unsigned long long Size
,unsigned long long *Actual
)
1239 *((char *)To
) = '\0';
1243 if (d
!= NULL
&& d
->gz
!= NULL
)
1244 Res
= gzread(d
->gz
,To
,Size
);
1248 if (d
!= NULL
&& d
->bz2
!= NULL
)
1249 Res
= BZ2_bzread(d
->bz2
,To
,Size
);
1252 Res
= read(iFd
,To
,Size
);
1259 if (d
!= NULL
&& d
->gz
!= NULL
)
1262 char const * const errmsg
= gzerror(d
->gz
, &err
);
1264 return FileFdError("gzread: %s (%d: %s)", _("Read error"), err
, errmsg
);
1268 if (d
!= NULL
&& d
->bz2
!= NULL
)
1271 char const * const errmsg
= BZ2_bzerror(d
->bz2
, &err
);
1272 if (err
!= BZ_IO_ERROR
)
1273 return FileFdError("BZ2_bzread: %s (%d: %s)", _("Read error"), err
, errmsg
);
1276 return FileFdErrno("read",_("Read error"));
1279 To
= (char *)To
+ Res
;
1286 while (Res
> 0 && Size
> 0);
1298 return FileFdError(_("read, still have %llu to read but none left"), Size
);
1301 // FileFd::ReadLine - Read a complete line from the file /*{{{*/
1302 // ---------------------------------------------------------------------
1303 /* Beware: This method can be quiet slow for big buffers on UNcompressed
1304 files because of the naive implementation! */
1305 char* FileFd::ReadLine(char *To
, unsigned long long const Size
)
1309 if (d
!= NULL
&& d
->gz
!= NULL
)
1310 return gzgets(d
->gz
, To
, Size
);
1313 unsigned long long read
= 0;
1314 while ((Size
- 1) != read
)
1316 unsigned long long done
= 0;
1317 if (Read(To
+ read
, 1, &done
) == false)
1321 if (To
[read
++] == '\n')
1330 // FileFd::Write - Write to the file /*{{{*/
1331 // ---------------------------------------------------------------------
1333 bool FileFd::Write(const void *From
,unsigned long long Size
)
1340 if (d
!= NULL
&& d
->gz
!= NULL
)
1341 Res
= gzwrite(d
->gz
,From
,Size
);
1345 if (d
!= NULL
&& d
->bz2
!= NULL
)
1346 Res
= BZ2_bzwrite(d
->bz2
,(void*)From
,Size
);
1349 Res
= write(iFd
,From
,Size
);
1350 if (Res
< 0 && errno
== EINTR
)
1355 if (d
!= NULL
&& d
->gz
!= NULL
)
1358 char const * const errmsg
= gzerror(d
->gz
, &err
);
1360 return FileFdError("gzwrite: %s (%d: %s)", _("Write error"), err
, errmsg
);
1364 if (d
!= NULL
&& d
->bz2
!= NULL
)
1367 char const * const errmsg
= BZ2_bzerror(d
->bz2
, &err
);
1368 if (err
!= BZ_IO_ERROR
)
1369 return FileFdError("BZ2_bzwrite: %s (%d: %s)", _("Write error"), err
, errmsg
);
1372 return FileFdErrno("write",_("Write error"));
1375 From
= (char *)From
+ Res
;
1380 while (Res
> 0 && Size
> 0);
1385 return FileFdError(_("write, still have %llu to write but couldn't"), Size
);
1387 bool FileFd::Write(int Fd
, const void *From
, unsigned long long Size
)
1393 Res
= write(Fd
,From
,Size
);
1394 if (Res
< 0 && errno
== EINTR
)
1397 return _error
->Errno("write",_("Write error"));
1399 From
= (char *)From
+ Res
;
1402 while (Res
> 0 && Size
> 0);
1407 return _error
->Error(_("write, still have %llu to write but couldn't"), Size
);
1410 // FileFd::Seek - Seek in the file /*{{{*/
1411 // ---------------------------------------------------------------------
1413 bool FileFd::Seek(unsigned long long To
)
1415 if (d
!= NULL
&& (d
->pipe
== true
1421 // Our poor man seeking in pipes is costly, so try to avoid it
1422 unsigned long long seekpos
= Tell();
1425 else if (seekpos
< To
)
1426 return Skip(To
- seekpos
);
1428 if ((d
->openmode
& ReadOnly
) != ReadOnly
)
1429 return FileFdError("Reopen is only implemented for read-only files!");
1433 BZ2_bzclose(d
->bz2
);
1440 if (TemporaryFileName
.empty() == false)
1441 iFd
= open(TemporaryFileName
.c_str(), O_RDONLY
);
1442 else if (FileName
.empty() == false)
1443 iFd
= open(FileName
.c_str(), O_RDONLY
);
1446 if (d
->compressed_fd
> 0)
1447 if (lseek(d
->compressed_fd
, 0, SEEK_SET
) != 0)
1448 iFd
= d
->compressed_fd
;
1450 return FileFdError("Reopen is not implemented for pipes opened with FileFd::OpenDescriptor()!");
1453 if (OpenInternDescriptor(d
->openmode
, d
->compressor
) == false)
1454 return FileFdError("Seek on file %s because it couldn't be reopened", FileName
.c_str());
1464 if (d
!= NULL
&& d
->gz
)
1465 res
= gzseek(d
->gz
,To
,SEEK_SET
);
1468 res
= lseek(iFd
,To
,SEEK_SET
);
1469 if (res
!= (signed)To
)
1470 return FileFdError("Unable to seek to %llu", To
);
1477 // FileFd::Skip - Seek in the file /*{{{*/
1478 // ---------------------------------------------------------------------
1480 bool FileFd::Skip(unsigned long long Over
)
1482 if (d
!= NULL
&& (d
->pipe
== true
1492 unsigned long long toread
= std::min((unsigned long long) sizeof(buffer
), Over
);
1493 if (Read(buffer
, toread
) == false)
1494 return FileFdError("Unable to seek ahead %llu",Over
);
1502 if (d
!= NULL
&& d
->gz
!= NULL
)
1503 res
= gzseek(d
->gz
,Over
,SEEK_CUR
);
1506 res
= lseek(iFd
,Over
,SEEK_CUR
);
1508 return FileFdError("Unable to seek ahead %llu",Over
);
1515 // FileFd::Truncate - Truncate the file /*{{{*/
1516 // ---------------------------------------------------------------------
1518 bool FileFd::Truncate(unsigned long long To
)
1520 #if defined HAVE_ZLIB || defined HAVE_BZ2
1521 if (d
!= NULL
&& (d
->gz
!= NULL
|| d
->bz2
!= NULL
))
1522 return FileFdError("Truncating compressed files is not implemented (%s)", FileName
.c_str());
1524 if (ftruncate(iFd
,To
) != 0)
1525 return FileFdError("Unable to truncate to %llu",To
);
1530 // FileFd::Tell - Current seek position /*{{{*/
1531 // ---------------------------------------------------------------------
1533 unsigned long long FileFd::Tell()
1535 // In theory, we could just return seekpos here always instead of
1536 // seeking around, but not all users of FileFd use always Seek() and co
1537 // so d->seekpos isn't always true and we can just use it as a hint if
1538 // we have nothing else, but not always as an authority…
1539 if (d
!= NULL
&& (d
->pipe
== true
1548 if (d
!= NULL
&& d
->gz
!= NULL
)
1549 Res
= gztell(d
->gz
);
1552 Res
= lseek(iFd
,0,SEEK_CUR
);
1553 if (Res
== (off_t
)-1)
1554 FileFdErrno("lseek","Failed to determine the current file position");
1560 // FileFd::FileSize - Return the size of the file /*{{{*/
1561 // ---------------------------------------------------------------------
1563 unsigned long long FileFd::FileSize()
1566 if ((d
== NULL
|| d
->pipe
== false) && fstat(iFd
,&Buf
) != 0)
1567 return FileFdErrno("fstat","Unable to determine the file size");
1569 // for compressor pipes st_size is undefined and at 'best' zero
1570 if ((d
!= NULL
&& d
->pipe
== true) || S_ISFIFO(Buf
.st_mode
))
1572 // we set it here, too, as we get the info here for free
1573 // in theory the Open-methods should take care of it already
1576 if (stat(FileName
.c_str(), &Buf
) != 0)
1577 return FileFdErrno("stat","Unable to determine the file size");
1583 // FileFd::Size - Return the size of the content in the file /*{{{*/
1584 // ---------------------------------------------------------------------
1586 unsigned long long FileFd::Size()
1588 unsigned long long size
= FileSize();
1590 // for compressor pipes st_size is undefined and at 'best' zero,
1591 // so we 'read' the content and 'seek' back - see there
1592 if (d
!= NULL
&& (d
->pipe
== true
1594 || (d
->bz2
&& size
> 0)
1598 unsigned long long const oldSeek
= Tell();
1600 unsigned long long read
= 0;
1602 Read(ignore
, sizeof(ignore
), &read
);
1608 // only check gzsize if we are actually a gzip file, just checking for
1609 // "gz" is not sufficient as uncompressed files could be opened with
1610 // gzopen in "direct" mode as well
1611 else if (d
!= NULL
&& d
->gz
&& !gzdirect(d
->gz
) && size
> 0)
1613 off_t
const oldPos
= lseek(iFd
,0,SEEK_CUR
);
1614 /* unfortunately zlib.h doesn't provide a gzsize(), so we have to do
1615 * this ourselves; the original (uncompressed) file size is the last 32
1616 * bits of the file */
1617 // FIXME: Size for gz-files is limited by 32bit… no largefile support
1618 if (lseek(iFd
, -4, SEEK_END
) < 0)
1619 return FileFdErrno("lseek","Unable to seek to end of gzipped file");
1621 if (read(iFd
, &size
, 4) != 4)
1622 return FileFdErrno("read","Unable to read original size of gzipped file");
1624 #ifdef WORDS_BIGENDIAN
1625 uint32_t tmp_size
= size
;
1626 uint8_t const * const p
= (uint8_t const * const) &tmp_size
;
1627 tmp_size
= (p
[3] << 24) | (p
[2] << 16) | (p
[1] << 8) | p
[0];
1631 if (lseek(iFd
, oldPos
, SEEK_SET
) < 0)
1632 return FileFdErrno("lseek","Unable to seek in gzipped file");
1641 // FileFd::ModificationTime - Return the time of last touch /*{{{*/
1642 // ---------------------------------------------------------------------
1644 time_t FileFd::ModificationTime()
1647 if ((d
== NULL
|| d
->pipe
== false) && fstat(iFd
,&Buf
) != 0)
1649 FileFdErrno("fstat","Unable to determine the modification time of file %s", FileName
.c_str());
1653 // for compressor pipes st_size is undefined and at 'best' zero
1654 if ((d
!= NULL
&& d
->pipe
== true) || S_ISFIFO(Buf
.st_mode
))
1656 // we set it here, too, as we get the info here for free
1657 // in theory the Open-methods should take care of it already
1660 if (stat(FileName
.c_str(), &Buf
) != 0)
1662 FileFdErrno("fstat","Unable to determine the modification time of file %s", FileName
.c_str());
1667 return Buf
.st_mtime
;
1670 // FileFd::Close - Close the file if the close flag is set /*{{{*/
1671 // ---------------------------------------------------------------------
1673 bool FileFd::Close()
1679 if ((Flags
& AutoClose
) == AutoClose
)
1681 if ((Flags
& Compressed
) != Compressed
&& iFd
> 0 && close(iFd
) != 0)
1682 Res
&= _error
->Errno("close",_("Problem closing the file %s"), FileName
.c_str());
1686 Res
&= d
->CloseDown(FileName
);
1692 if ((Flags
& Replace
) == Replace
) {
1693 if (rename(TemporaryFileName
.c_str(), FileName
.c_str()) != 0)
1694 Res
&= _error
->Errno("rename",_("Problem renaming the file %s to %s"), TemporaryFileName
.c_str(), FileName
.c_str());
1696 FileName
= TemporaryFileName
; // for the unlink() below.
1697 TemporaryFileName
.clear();
1702 if ((Flags
& Fail
) == Fail
&& (Flags
& DelOnFail
) == DelOnFail
&&
1703 FileName
.empty() == false)
1704 if (unlink(FileName
.c_str()) != 0)
1705 Res
&= _error
->WarningE("unlnk",_("Problem unlinking the file %s"), FileName
.c_str());
1712 // FileFd::Sync - Sync the file /*{{{*/
1713 // ---------------------------------------------------------------------
1717 if (fsync(iFd
) != 0)
1718 return FileFdErrno("sync",_("Problem syncing the file"));
1722 // FileFd::FileFdErrno - set Fail and call _error->Errno *{{{*/
1723 bool FileFd::FileFdErrno(const char *Function
, const char *Description
,...)
1727 size_t msgSize
= 400;
1728 int const errsv
= errno
;
1731 va_start(args
,Description
);
1732 if (_error
->InsertErrno(GlobalError::ERROR
, Function
, Description
, args
, errsv
, msgSize
) == false)
1739 // FileFd::FileFdError - set Fail and call _error->Error *{{{*/
1740 bool FileFd::FileFdError(const char *Description
,...) {
1743 size_t msgSize
= 400;
1746 va_start(args
,Description
);
1747 if (_error
->Insert(GlobalError::ERROR
, Description
, args
, msgSize
) == false)
1755 gzFile
FileFd::gzFd() { return (gzFile
) d
->gz
; }