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>
47 // FIXME: Compressor Fds have some speed disadvantages and are a bit buggy currently,
48 // so while the current implementation satisfies the testcases it is not a real option
49 // to disable it for now
50 #define APT_USE_ZLIB 1
54 #pragma message "Usage of zlib is DISABLED!"
57 #ifdef WORDS_BIGENDIAN
76 APT::Configuration::Compressor compressor
;
77 unsigned int openmode
;
78 unsigned long long seekpos
;
79 FileFdPrivate() : gz(NULL
), compressed_fd(-1), compressor_pid(-1), pipe(false),
80 openmode(0), seekpos(0) {};
83 // RunScripts - Run a set of scripts from a configuration subtree /*{{{*/
84 // ---------------------------------------------------------------------
86 bool RunScripts(const char *Cnf
)
88 Configuration::Item
const *Opts
= _config
->Tree(Cnf
);
89 if (Opts
== 0 || Opts
->Child
== 0)
93 // Fork for running the system calls
94 pid_t Child
= ExecFork();
99 if (_config
->FindDir("DPkg::Chroot-Directory","/") != "/")
101 std::cerr
<< "Chrooting into "
102 << _config
->FindDir("DPkg::Chroot-Directory")
104 if (chroot(_config
->FindDir("DPkg::Chroot-Directory","/").c_str()) != 0)
108 if (chdir("/tmp/") != 0)
111 unsigned int Count
= 1;
112 for (; Opts
!= 0; Opts
= Opts
->Next
, Count
++)
114 if (Opts
->Value
.empty() == true)
117 if (system(Opts
->Value
.c_str()) != 0)
123 // Wait for the child
125 while (waitpid(Child
,&Status
,0) != Child
)
129 return _error
->Errno("waitpid","Couldn't wait for subprocess");
132 // Restore sig int/quit
133 signal(SIGQUIT
,SIG_DFL
);
134 signal(SIGINT
,SIG_DFL
);
136 // Check for an error code.
137 if (WIFEXITED(Status
) == 0 || WEXITSTATUS(Status
) != 0)
139 unsigned int Count
= WEXITSTATUS(Status
);
143 for (; Opts
!= 0 && Count
!= 1; Opts
= Opts
->Next
, Count
--);
144 _error
->Error("Problem executing scripts %s '%s'",Cnf
,Opts
->Value
.c_str());
147 return _error
->Error("Sub-process returned an error code");
154 // CopyFile - Buffered copy of a file /*{{{*/
155 // ---------------------------------------------------------------------
156 /* The caller is expected to set things so that failure causes erasure */
157 bool CopyFile(FileFd
&From
,FileFd
&To
)
159 if (From
.IsOpen() == false || To
.IsOpen() == false)
162 // Buffered copy between fds
163 SPtrArray
<unsigned char> Buf
= new unsigned char[64000];
164 unsigned long long Size
= From
.Size();
167 unsigned long long ToRead
= Size
;
171 if (From
.Read(Buf
,ToRead
) == false ||
172 To
.Write(Buf
,ToRead
) == false)
181 // GetLock - Gets a lock file /*{{{*/
182 // ---------------------------------------------------------------------
183 /* This will create an empty file of the given name and lock it. Once this
184 is done all other calls to GetLock in any other process will fail with
185 -1. The return result is the fd of the file, the call should call
186 close at some time. */
187 int GetLock(string File
,bool Errors
)
189 // GetLock() is used in aptitude on directories with public-write access
190 // Use O_NOFOLLOW here to prevent symlink traversal attacks
191 int FD
= open(File
.c_str(),O_RDWR
| O_CREAT
| O_NOFOLLOW
,0640);
194 // Read only .. cant have locking problems there.
197 _error
->Warning(_("Not using locking for read only lock file %s"),File
.c_str());
198 return dup(0); // Need something for the caller to close
202 _error
->Errno("open",_("Could not open lock file %s"),File
.c_str());
204 // Feh.. We do this to distinguish the lock vs open case..
208 SetCloseExec(FD
,true);
210 // Aquire a write lock
213 fl
.l_whence
= SEEK_SET
;
216 if (fcntl(FD
,F_SETLK
,&fl
) == -1)
220 _error
->Warning(_("Not using locking for nfs mounted lock file %s"),File
.c_str());
221 return dup(0); // Need something for the caller to close
224 _error
->Errno("open",_("Could not get lock %s"),File
.c_str());
235 // FileExists - Check if a file exists /*{{{*/
236 // ---------------------------------------------------------------------
237 /* Beware: Directories are also files! */
238 bool FileExists(string File
)
241 if (stat(File
.c_str(),&Buf
) != 0)
246 // RealFileExists - Check if a file exists and if it is really a file /*{{{*/
247 // ---------------------------------------------------------------------
249 bool RealFileExists(string File
)
252 if (stat(File
.c_str(),&Buf
) != 0)
254 return ((Buf
.st_mode
& S_IFREG
) != 0);
257 // DirectoryExists - Check if a directory exists and is really one /*{{{*/
258 // ---------------------------------------------------------------------
260 bool DirectoryExists(string
const &Path
)
263 if (stat(Path
.c_str(),&Buf
) != 0)
265 return ((Buf
.st_mode
& S_IFDIR
) != 0);
268 // CreateDirectory - poor man's mkdir -p guarded by a parent directory /*{{{*/
269 // ---------------------------------------------------------------------
270 /* This method will create all directories needed for path in good old
271 mkdir -p style but refuses to do this if Parent is not a prefix of
272 this Path. Example: /var/cache/ and /var/cache/apt/archives are given,
273 so it will create apt/archives if /var/cache exists - on the other
274 hand if the parent is /var/lib the creation will fail as this path
275 is not a parent of the path to be generated. */
276 bool CreateDirectory(string
const &Parent
, string
const &Path
)
278 if (Parent
.empty() == true || Path
.empty() == true)
281 if (DirectoryExists(Path
) == true)
284 if (DirectoryExists(Parent
) == false)
287 // we are not going to create directories "into the blue"
288 if (Path
.find(Parent
, 0) != 0)
291 vector
<string
> const dirs
= VectorizeString(Path
.substr(Parent
.size()), '/');
292 string progress
= Parent
;
293 for (vector
<string
>::const_iterator d
= dirs
.begin(); d
!= dirs
.end(); ++d
)
295 if (d
->empty() == true)
298 progress
.append("/").append(*d
);
299 if (DirectoryExists(progress
) == true)
302 if (mkdir(progress
.c_str(), 0755) != 0)
308 // CreateAPTDirectoryIfNeeded - ensure that the given directory exists /*{{{*/
309 // ---------------------------------------------------------------------
310 /* a small wrapper around CreateDirectory to check if it exists and to
311 remove the trailing "/apt/" from the parent directory if needed */
312 bool CreateAPTDirectoryIfNeeded(string
const &Parent
, string
const &Path
)
314 if (DirectoryExists(Path
) == true)
317 size_t const len
= Parent
.size();
318 if (len
> 5 && Parent
.find("/apt/", len
- 6, 5) == len
- 5)
320 if (CreateDirectory(Parent
.substr(0,len
-5), Path
) == true)
323 else if (CreateDirectory(Parent
, Path
) == true)
329 // GetListOfFilesInDir - returns a vector of files in the given dir /*{{{*/
330 // ---------------------------------------------------------------------
331 /* If an extension is given only files with this extension are included
332 in the returned vector, otherwise every "normal" file is included. */
333 std::vector
<string
> GetListOfFilesInDir(string
const &Dir
, string
const &Ext
,
334 bool const &SortList
, bool const &AllowNoExt
)
336 std::vector
<string
> ext
;
338 if (Ext
.empty() == false)
340 if (AllowNoExt
== true && ext
.empty() == false)
342 return GetListOfFilesInDir(Dir
, ext
, SortList
);
344 std::vector
<string
> GetListOfFilesInDir(string
const &Dir
, std::vector
<string
> const &Ext
,
345 bool const &SortList
)
347 // Attention debuggers: need to be set with the environment config file!
348 bool const Debug
= _config
->FindB("Debug::GetListOfFilesInDir", false);
351 std::clog
<< "Accept in " << Dir
<< " only files with the following " << Ext
.size() << " extensions:" << std::endl
;
352 if (Ext
.empty() == true)
353 std::clog
<< "\tNO extension" << std::endl
;
355 for (std::vector
<string
>::const_iterator e
= Ext
.begin();
357 std::clog
<< '\t' << (e
->empty() == true ? "NO" : *e
) << " extension" << std::endl
;
360 std::vector
<string
> List
;
362 if (DirectoryExists(Dir
.c_str()) == false)
364 _error
->Error(_("List of files can't be created as '%s' is not a directory"), Dir
.c_str());
368 Configuration::MatchAgainstConfig
SilentIgnore("Dir::Ignore-Files-Silently");
369 DIR *D
= opendir(Dir
.c_str());
372 _error
->Errno("opendir",_("Unable to read %s"),Dir
.c_str());
376 for (struct dirent
*Ent
= readdir(D
); Ent
!= 0; Ent
= readdir(D
))
378 // skip "hidden" files
379 if (Ent
->d_name
[0] == '.')
382 // Make sure it is a file and not something else
383 string
const File
= flCombine(Dir
,Ent
->d_name
);
384 #ifdef _DIRENT_HAVE_D_TYPE
385 if (Ent
->d_type
!= DT_REG
)
388 if (RealFileExists(File
.c_str()) == false)
390 if (SilentIgnore
.Match(Ent
->d_name
) == false)
391 _error
->Notice(_("Ignoring '%s' in directory '%s' as it is not a regular file"), Ent
->d_name
, Dir
.c_str());
396 // check for accepted extension:
397 // no extension given -> periods are bad as hell!
398 // extensions given -> "" extension allows no extension
399 if (Ext
.empty() == false)
401 string d_ext
= flExtension(Ent
->d_name
);
402 if (d_ext
== Ent
->d_name
) // no extension
404 if (std::find(Ext
.begin(), Ext
.end(), "") == Ext
.end())
407 std::clog
<< "Bad file: " << Ent
->d_name
<< " → no extension" << std::endl
;
408 if (SilentIgnore
.Match(Ent
->d_name
) == false)
409 _error
->Notice(_("Ignoring file '%s' in directory '%s' as it has no filename extension"), Ent
->d_name
, Dir
.c_str());
413 else if (std::find(Ext
.begin(), Ext
.end(), d_ext
) == Ext
.end())
416 std::clog
<< "Bad file: " << Ent
->d_name
<< " → bad extension »" << flExtension(Ent
->d_name
) << "«" << std::endl
;
417 if (SilentIgnore
.Match(Ent
->d_name
) == false)
418 _error
->Notice(_("Ignoring file '%s' in directory '%s' as it has an invalid filename extension"), Ent
->d_name
, Dir
.c_str());
423 // Skip bad filenames ala run-parts
424 const char *C
= Ent
->d_name
;
426 if (isalpha(*C
) == 0 && isdigit(*C
) == 0
427 && *C
!= '_' && *C
!= '-') {
428 // no required extension -> dot is a bad character
429 if (*C
== '.' && Ext
.empty() == false)
434 // we don't reach the end of the name -> bad character included
438 std::clog
<< "Bad file: " << Ent
->d_name
<< " → bad character »"
439 << *C
<< "« in filename (period allowed: " << (Ext
.empty() ? "no" : "yes") << ")" << std::endl
;
443 // skip filenames which end with a period. These are never valid
447 std::clog
<< "Bad file: " << Ent
->d_name
<< " → Period as last character" << std::endl
;
452 std::clog
<< "Accept file: " << Ent
->d_name
<< " in " << Dir
<< std::endl
;
453 List
.push_back(File
);
457 if (SortList
== true)
458 std::sort(List
.begin(),List
.end());
461 std::vector
<string
> GetListOfFilesInDir(string
const &Dir
, bool SortList
)
463 bool const Debug
= _config
->FindB("Debug::GetListOfFilesInDir", false);
465 std::clog
<< "Accept in " << Dir
<< " all regular files" << std::endl
;
467 std::vector
<string
> List
;
469 if (DirectoryExists(Dir
.c_str()) == false)
471 _error
->Error(_("List of files can't be created as '%s' is not a directory"), Dir
.c_str());
475 DIR *D
= opendir(Dir
.c_str());
478 _error
->Errno("opendir",_("Unable to read %s"),Dir
.c_str());
482 for (struct dirent
*Ent
= readdir(D
); Ent
!= 0; Ent
= readdir(D
))
484 // skip "hidden" files
485 if (Ent
->d_name
[0] == '.')
488 // Make sure it is a file and not something else
489 string
const File
= flCombine(Dir
,Ent
->d_name
);
490 #ifdef _DIRENT_HAVE_D_TYPE
491 if (Ent
->d_type
!= DT_REG
)
494 if (RealFileExists(File
.c_str()) == false)
497 std::clog
<< "Bad file: " << Ent
->d_name
<< " → it is not a real file" << std::endl
;
502 // Skip bad filenames ala run-parts
503 const char *C
= Ent
->d_name
;
505 if (isalpha(*C
) == 0 && isdigit(*C
) == 0
506 && *C
!= '_' && *C
!= '-' && *C
!= '.')
509 // we don't reach the end of the name -> bad character included
513 std::clog
<< "Bad file: " << Ent
->d_name
<< " → bad character »" << *C
<< "« in filename" << std::endl
;
517 // skip filenames which end with a period. These are never valid
521 std::clog
<< "Bad file: " << Ent
->d_name
<< " → Period as last character" << std::endl
;
526 std::clog
<< "Accept file: " << Ent
->d_name
<< " in " << Dir
<< std::endl
;
527 List
.push_back(File
);
531 if (SortList
== true)
532 std::sort(List
.begin(),List
.end());
536 // SafeGetCWD - This is a safer getcwd that returns a dynamic string /*{{{*/
537 // ---------------------------------------------------------------------
538 /* We return / on failure. */
541 // Stash the current dir.
544 if (getcwd(S
,sizeof(S
)-2) == 0)
546 unsigned int Len
= strlen(S
);
552 // GetModificationTime - Get the mtime of the given file or -1 on error /*{{{*/
553 // ---------------------------------------------------------------------
554 /* We return / on failure. */
555 time_t GetModificationTime(string
const &Path
)
558 if (stat(Path
.c_str(), &St
) < 0)
563 // flNotDir - Strip the directory from the filename /*{{{*/
564 // ---------------------------------------------------------------------
566 string
flNotDir(string File
)
568 string::size_type Res
= File
.rfind('/');
569 if (Res
== string::npos
)
572 return string(File
,Res
,Res
- File
.length());
575 // flNotFile - Strip the file from the directory name /*{{{*/
576 // ---------------------------------------------------------------------
577 /* Result ends in a / */
578 string
flNotFile(string File
)
580 string::size_type Res
= File
.rfind('/');
581 if (Res
== string::npos
)
584 return string(File
,0,Res
);
587 // flExtension - Return the extension for the file /*{{{*/
588 // ---------------------------------------------------------------------
590 string
flExtension(string File
)
592 string::size_type Res
= File
.rfind('.');
593 if (Res
== string::npos
)
596 return string(File
,Res
,Res
- File
.length());
599 // flNoLink - If file is a symlink then deref it /*{{{*/
600 // ---------------------------------------------------------------------
601 /* If the name is not a link then the returned path is the input. */
602 string
flNoLink(string File
)
605 if (lstat(File
.c_str(),&St
) != 0 || S_ISLNK(St
.st_mode
) == 0)
607 if (stat(File
.c_str(),&St
) != 0)
610 /* Loop resolving the link. There is no need to limit the number of
611 loops because the stat call above ensures that the symlink is not
619 if ((Res
= readlink(NFile
.c_str(),Buffer
,sizeof(Buffer
))) <= 0 ||
620 (unsigned)Res
>= sizeof(Buffer
))
623 // Append or replace the previous path
625 if (Buffer
[0] == '/')
628 NFile
= flNotFile(NFile
) + Buffer
;
630 // See if we are done
631 if (lstat(NFile
.c_str(),&St
) != 0)
633 if (S_ISLNK(St
.st_mode
) == 0)
638 // flCombine - Combine a file and a directory /*{{{*/
639 // ---------------------------------------------------------------------
640 /* If the file is an absolute path then it is just returned, otherwise
641 the directory is pre-pended to it. */
642 string
flCombine(string Dir
,string File
)
644 if (File
.empty() == true)
647 if (File
[0] == '/' || Dir
.empty() == true)
649 if (File
.length() >= 2 && File
[0] == '.' && File
[1] == '/')
651 if (Dir
[Dir
.length()-1] == '/')
653 return Dir
+ '/' + File
;
656 // SetCloseExec - Set the close on exec flag /*{{{*/
657 // ---------------------------------------------------------------------
659 void SetCloseExec(int Fd
,bool Close
)
661 if (fcntl(Fd
,F_SETFD
,(Close
== false)?0:FD_CLOEXEC
) != 0)
663 cerr
<< "FATAL -> Could not set close on exec " << strerror(errno
) << endl
;
668 // SetNonBlock - Set the nonblocking flag /*{{{*/
669 // ---------------------------------------------------------------------
671 void SetNonBlock(int Fd
,bool Block
)
673 int Flags
= fcntl(Fd
,F_GETFL
) & (~O_NONBLOCK
);
674 if (fcntl(Fd
,F_SETFL
,Flags
| ((Block
== false)?0:O_NONBLOCK
)) != 0)
676 cerr
<< "FATAL -> Could not set non-blocking flag " << strerror(errno
) << endl
;
681 // WaitFd - Wait for a FD to become readable /*{{{*/
682 // ---------------------------------------------------------------------
683 /* This waits for a FD to become readable using select. It is useful for
684 applications making use of non-blocking sockets. The timeout is
686 bool WaitFd(int Fd
,bool write
,unsigned long timeout
)
699 Res
= select(Fd
+1,0,&Set
,0,(timeout
!= 0?&tv
:0));
701 while (Res
< 0 && errno
== EINTR
);
711 Res
= select(Fd
+1,&Set
,0,0,(timeout
!= 0?&tv
:0));
713 while (Res
< 0 && errno
== EINTR
);
722 // ExecFork - Magical fork that sanitizes the context before execing /*{{{*/
723 // ---------------------------------------------------------------------
724 /* This is used if you want to cleanse the environment for the forked
725 child, it fixes up the important signals and nukes all of the fds,
726 otherwise acts like normal fork. */
729 // Fork off the process
730 pid_t Process
= fork();
733 cerr
<< "FATAL -> Failed to fork." << endl
;
737 // Spawn the subprocess
741 signal(SIGPIPE
,SIG_DFL
);
742 signal(SIGQUIT
,SIG_DFL
);
743 signal(SIGINT
,SIG_DFL
);
744 signal(SIGWINCH
,SIG_DFL
);
745 signal(SIGCONT
,SIG_DFL
);
746 signal(SIGTSTP
,SIG_DFL
);
749 Configuration::Item
const *Opts
= _config
->Tree("APT::Keep-Fds");
750 if (Opts
!= 0 && Opts
->Child
!= 0)
753 for (; Opts
!= 0; Opts
= Opts
->Next
)
755 if (Opts
->Value
.empty() == true)
757 int fd
= atoi(Opts
->Value
.c_str());
762 // Close all of our FDs - just in case
763 for (int K
= 3; K
!= 40; K
++)
765 if(KeepFDs
.find(K
) == KeepFDs
.end())
766 fcntl(K
,F_SETFD
,FD_CLOEXEC
);
773 // ExecWait - Fancy waitpid /*{{{*/
774 // ---------------------------------------------------------------------
775 /* Waits for the given sub process. If Reap is set then no errors are
776 generated. Otherwise a failed subprocess will generate a proper descriptive
778 bool ExecWait(pid_t Pid
,const char *Name
,bool Reap
)
783 // Wait and collect the error code
785 while (waitpid(Pid
,&Status
,0) != Pid
)
793 return _error
->Error(_("Waited for %s but it wasn't there"),Name
);
797 // Check for an error code.
798 if (WIFEXITED(Status
) == 0 || WEXITSTATUS(Status
) != 0)
802 if (WIFSIGNALED(Status
) != 0)
804 if( WTERMSIG(Status
) == SIGSEGV
)
805 return _error
->Error(_("Sub-process %s received a segmentation fault."),Name
);
807 return _error
->Error(_("Sub-process %s received signal %u."),Name
, WTERMSIG(Status
));
810 if (WIFEXITED(Status
) != 0)
811 return _error
->Error(_("Sub-process %s returned an error code (%u)"),Name
,WEXITSTATUS(Status
));
813 return _error
->Error(_("Sub-process %s exited unexpectedly"),Name
);
820 // FileFd::Open - Open a file /*{{{*/
821 // ---------------------------------------------------------------------
822 /* The most commonly used open mode combinations are given with Mode */
823 bool FileFd::Open(string FileName
,unsigned int const Mode
,CompressMode Compress
, unsigned long const Perms
)
825 if (Mode
== ReadOnlyGzip
)
826 return Open(FileName
, ReadOnly
, Gzip
, Perms
);
828 if (Compress
== Auto
&& (Mode
& WriteOnly
) == WriteOnly
)
829 return _error
->Error("Autodetection on %s only works in ReadOnly openmode!", FileName
.c_str());
831 // FIXME: Denote inbuilt compressors somehow - as we don't need to have the binaries for them
832 std::vector
<APT::Configuration::Compressor
> const compressors
= APT::Configuration::getCompressors();
833 std::vector
<APT::Configuration::Compressor
>::const_iterator compressor
= compressors
.begin();
834 if (Compress
== Auto
)
836 for (; compressor
!= compressors
.end(); ++compressor
)
838 std::string file
= std::string(FileName
).append(compressor
->Extension
);
839 if (FileExists(file
) == false)
845 else if (Compress
== Extension
)
847 std::string::size_type
const found
= FileName
.find_last_of('.');
849 if (found
!= std::string::npos
)
851 ext
= FileName
.substr(found
);
852 if (ext
== ".new" || ext
== ".bak")
854 std::string::size_type
const found2
= FileName
.find_last_of('.', found
- 1);
855 if (found2
!= std::string::npos
)
856 ext
= FileName
.substr(found2
, found
- found2
);
861 for (; compressor
!= compressors
.end(); ++compressor
)
862 if (ext
== compressor
->Extension
)
864 // no matching extension - assume uncompressed (imagine files like 'example.org_Packages')
865 if (compressor
== compressors
.end())
866 for (compressor
= compressors
.begin(); compressor
!= compressors
.end(); ++compressor
)
867 if (compressor
->Name
== ".")
875 case None
: name
= "."; break;
876 case Gzip
: name
= "gzip"; break;
877 case Bzip2
: name
= "bzip2"; break;
878 case Lzma
: name
= "lzma"; break;
879 case Xz
: name
= "xz"; break;
883 return _error
->Error("Opening File %s in None, Auto or Extension should be already handled?!?", FileName
.c_str());
885 for (; compressor
!= compressors
.end(); ++compressor
)
886 if (compressor
->Name
== name
)
888 if (compressor
== compressors
.end())
889 return _error
->Error("Can't find a configured compressor %s for file %s", name
.c_str(), FileName
.c_str());
892 if (compressor
== compressors
.end())
893 return _error
->Error("Can't find a match for specified compressor mode for file %s", FileName
.c_str());
894 return Open(FileName
, Mode
, *compressor
, Perms
);
896 bool FileFd::Open(string FileName
,unsigned int const Mode
,APT::Configuration::Compressor
const &compressor
, unsigned long const Perms
)
899 d
= new FileFdPrivate
;
903 if ((Mode
& WriteOnly
) != WriteOnly
&& (Mode
& (Atomic
| Create
| Empty
| Exclusive
)) != 0)
904 return _error
->Error("ReadOnly mode for %s doesn't accept additional flags!", FileName
.c_str());
905 if ((Mode
& ReadWrite
) == 0)
906 return _error
->Error("No openmode provided in FileFd::Open for %s", FileName
.c_str());
908 if ((Mode
& Atomic
) == Atomic
)
911 char *name
= strdup((FileName
+ ".XXXXXX").c_str());
912 TemporaryFileName
= string(mktemp(name
));
915 else if ((Mode
& (Exclusive
| Create
)) == (Exclusive
| Create
))
917 // for atomic, this will be done by rename in Close()
918 unlink(FileName
.c_str());
920 if ((Mode
& Empty
) == Empty
)
923 if (lstat(FileName
.c_str(),&Buf
) == 0 && S_ISLNK(Buf
.st_mode
))
924 unlink(FileName
.c_str());
928 #define if_FLAGGED_SET(FLAG, MODE) if ((Mode & FLAG) == FLAG) fileflags |= MODE
929 if_FLAGGED_SET(ReadWrite
, O_RDWR
);
930 else if_FLAGGED_SET(ReadOnly
, O_RDONLY
);
931 else if_FLAGGED_SET(WriteOnly
, O_WRONLY
);
933 if_FLAGGED_SET(Create
, O_CREAT
);
934 if_FLAGGED_SET(Empty
, O_TRUNC
);
935 if_FLAGGED_SET(Exclusive
, O_EXCL
);
936 else if_FLAGGED_SET(Atomic
, O_EXCL
);
937 #undef if_FLAGGED_SET
939 if (TemporaryFileName
.empty() == false)
940 iFd
= open(TemporaryFileName
.c_str(), fileflags
, Perms
);
942 iFd
= open(FileName
.c_str(), fileflags
, Perms
);
944 this->FileName
= FileName
;
945 if (iFd
== -1 || OpenInternDescriptor(Mode
, compressor
) == false)
952 return _error
->Errno("open",_("Could not open file %s"), FileName
.c_str());
955 SetCloseExec(iFd
,true);
959 // FileFd::OpenDescriptor - Open a filedescriptor /*{{{*/
960 // ---------------------------------------------------------------------
962 bool FileFd::OpenDescriptor(int Fd
, unsigned int const Mode
, CompressMode Compress
, bool AutoClose
)
964 std::vector
<APT::Configuration::Compressor
> const compressors
= APT::Configuration::getCompressors();
965 std::vector
<APT::Configuration::Compressor
>::const_iterator compressor
= compressors
.begin();
968 // compat with the old API
969 if (Mode
== ReadOnlyGzip
&& Compress
== None
)
974 case None
: name
= "."; break;
975 case Gzip
: name
= "gzip"; break;
976 case Bzip2
: name
= "bzip2"; break;
977 case Lzma
: name
= "lzma"; break;
978 case Xz
: name
= "xz"; break;
981 return _error
->Error("Opening Fd %d in Auto or Extension compression mode is not supported", Fd
);
983 for (; compressor
!= compressors
.end(); ++compressor
)
984 if (compressor
->Name
== name
)
986 if (compressor
== compressors
.end())
987 return _error
->Error("Can't find a configured compressor %s for file %s", name
.c_str(), FileName
.c_str());
989 return OpenDescriptor(Fd
, Mode
, *compressor
, AutoClose
);
991 bool FileFd::OpenDescriptor(int Fd
, unsigned int const Mode
, APT::Configuration::Compressor
const &compressor
, bool AutoClose
)
994 d
= new FileFdPrivate
;
996 Flags
= (AutoClose
) ? FileFd::AutoClose
: 0;
999 if (OpenInternDescriptor(Mode
, compressor
) == false)
1003 return _error
->Errno("gzdopen",_("Could not open file descriptor %d"), Fd
);
1007 bool FileFd::OpenInternDescriptor(unsigned int const Mode
, APT::Configuration::Compressor
const &compressor
)
1009 d
->compressor
= compressor
;
1010 if (compressor
.Name
== "." || compressor
.Binary
.empty() == true)
1013 else if (compressor
.Name
== "gzip")
1015 if ((Mode
& ReadWrite
) == ReadWrite
)
1016 d
->gz
= gzdopen(iFd
, "r+");
1017 else if ((Mode
& WriteOnly
) == WriteOnly
)
1018 d
->gz
= gzdopen(iFd
, "w");
1020 d
->gz
= gzdopen (iFd
, "r");
1023 Flags
|= Compressed
;
1028 if ((Mode
& ReadWrite
) == ReadWrite
)
1029 return _error
->Error("ReadWrite mode is not supported for file %s", FileName
.c_str());
1031 bool const Comp
= (Mode
& WriteOnly
) == WriteOnly
;
1032 // Handle 'decompression' of empty files
1037 if (Buf
.st_size
== 0 && S_ISFIFO(Buf
.st_mode
) == false)
1040 // We don't need the file open - instead let the compressor open it
1041 // as he properly knows better how to efficiently read from 'his' file
1042 if (FileName
.empty() == false)
1046 // Create a data pipe
1047 int Pipe
[2] = {-1,-1};
1048 if (pipe(Pipe
) != 0)
1049 return _error
->Errno("pipe",_("Failed to create subprocess IPC"));
1050 for (int J
= 0; J
!= 2; J
++)
1051 SetCloseExec(Pipe
[J
],true);
1053 d
->compressed_fd
= iFd
;
1062 d
->compressor_pid
= ExecFork();
1063 if (d
->compressor_pid
== 0)
1067 dup2(d
->compressed_fd
,STDOUT_FILENO
);
1068 dup2(Pipe
[0],STDIN_FILENO
);
1072 if (FileName
.empty() == true)
1073 dup2(d
->compressed_fd
,STDIN_FILENO
);
1074 dup2(Pipe
[1],STDOUT_FILENO
);
1077 SetCloseExec(STDOUT_FILENO
,false);
1078 SetCloseExec(STDIN_FILENO
,false);
1080 std::vector
<char const*> Args
;
1081 Args
.push_back(compressor
.Binary
.c_str());
1082 std::vector
<std::string
> const * const addArgs
=
1083 (Comp
== true) ? &(compressor
.CompressArgs
) : &(compressor
.UncompressArgs
);
1084 for (std::vector
<std::string
>::const_iterator a
= addArgs
->begin();
1085 a
!= addArgs
->end(); ++a
)
1086 Args
.push_back(a
->c_str());
1087 if (Comp
== false && FileName
.empty() == false)
1089 Args
.push_back("--stdout");
1090 if (TemporaryFileName
.empty() == false)
1091 Args
.push_back(TemporaryFileName
.c_str());
1093 Args
.push_back(FileName
.c_str());
1095 Args
.push_back(NULL
);
1097 execvp(Args
[0],(char **)&Args
[0]);
1098 cerr
<< _("Failed to exec compressor ") << Args
[0] << endl
;
1105 if (Comp
== true || FileName
.empty() == true)
1106 close(d
->compressed_fd
);
1111 // FileFd::~File - Closes the file /*{{{*/
1112 // ---------------------------------------------------------------------
1113 /* If the proper modes are selected then we close the Fd and possibly
1114 unlink the file on error. */
1120 // FileFd::Read - Read a bit of the file /*{{{*/
1121 // ---------------------------------------------------------------------
1122 /* We are carefull to handle interruption by a signal while reading
1124 bool FileFd::Read(void *To
,unsigned long long Size
,unsigned long long *Actual
)
1130 *((char *)To
) = '\0';
1135 Res
= gzread(d
->gz
,To
,Size
);
1138 Res
= read(iFd
,To
,Size
);
1149 char const * const errmsg
= gzerror(d
->gz
, &err
);
1151 return _error
->Error("gzread: %s (%d: %s)", _("Read error"), err
, errmsg
);
1154 return _error
->Errno("read",_("Read error"));
1157 To
= (char *)To
+ Res
;
1163 while (Res
> 0 && Size
> 0);
1176 return _error
->Error(_("read, still have %llu to read but none left"), Size
);
1179 // FileFd::ReadLine - Read a complete line from the file /*{{{*/
1180 // ---------------------------------------------------------------------
1181 /* Beware: This method can be quiet slow for big buffers on UNcompressed
1182 files because of the naive implementation! */
1183 char* FileFd::ReadLine(char *To
, unsigned long long const Size
)
1188 return gzgets(d
->gz
, To
, Size
);
1191 unsigned long long read
= 0;
1192 while ((Size
- 1) != read
)
1194 unsigned long long done
= 0;
1195 if (Read(To
+ read
, 1, &done
) == false)
1199 if (To
[read
++] == '\n')
1208 // FileFd::Write - Write to the file /*{{{*/
1209 // ---------------------------------------------------------------------
1211 bool FileFd::Write(const void *From
,unsigned long long Size
)
1219 Res
= gzwrite(d
->gz
,From
,Size
);
1222 Res
= write(iFd
,From
,Size
);
1223 if (Res
< 0 && errno
== EINTR
)
1228 return _error
->Errno("write",_("Write error"));
1231 From
= (char *)From
+ Res
;
1235 while (Res
> 0 && Size
> 0);
1241 return _error
->Error(_("write, still have %llu to write but couldn't"), Size
);
1244 // FileFd::Seek - Seek in the file /*{{{*/
1245 // ---------------------------------------------------------------------
1247 bool FileFd::Seek(unsigned long long To
)
1249 if (d
->pipe
== true)
1251 // Our poor man seeking in pipes is costly, so try to avoid it
1252 unsigned long long seekpos
= Tell();
1255 else if (seekpos
< To
)
1256 return Skip(To
- seekpos
);
1258 if ((d
->openmode
& ReadOnly
) != ReadOnly
)
1259 return _error
->Error("Reopen is only implemented for read-only files!");
1262 if (TemporaryFileName
.empty() == false)
1263 iFd
= open(TemporaryFileName
.c_str(), O_RDONLY
);
1264 else if (FileName
.empty() == false)
1265 iFd
= open(FileName
.c_str(), O_RDONLY
);
1268 if (d
->compressed_fd
> 0)
1269 if (lseek(d
->compressed_fd
, 0, SEEK_SET
) != 0)
1270 iFd
= d
->compressed_fd
;
1272 return _error
->Error("Reopen is not implemented for pipes opened with FileFd::OpenDescriptor()!");
1275 if (OpenInternDescriptor(d
->openmode
, d
->compressor
) == false)
1276 return _error
->Error("Seek on file %s because it couldn't be reopened", FileName
.c_str());
1287 res
= gzseek(d
->gz
,To
,SEEK_SET
);
1290 res
= lseek(iFd
,To
,SEEK_SET
);
1291 if (res
!= (signed)To
)
1294 return _error
->Error("Unable to seek to %llu", To
);
1301 // FileFd::Skip - Seek in the file /*{{{*/
1302 // ---------------------------------------------------------------------
1304 bool FileFd::Skip(unsigned long long Over
)
1306 if (d
->pipe
== true)
1312 unsigned long long toread
= std::min((unsigned long long) sizeof(buffer
), Over
);
1313 if (Read(buffer
, toread
) == false)
1314 return _error
->Error("Unable to seek ahead %llu",Over
);
1323 res
= gzseek(d
->gz
,Over
,SEEK_CUR
);
1326 res
= lseek(iFd
,Over
,SEEK_CUR
);
1330 return _error
->Error("Unable to seek ahead %llu",Over
);
1337 // FileFd::Truncate - Truncate the file /*{{{*/
1338 // ---------------------------------------------------------------------
1340 bool FileFd::Truncate(unsigned long long To
)
1345 return _error
->Error("Truncating gzipped files is not implemented (%s)", FileName
.c_str());
1347 if (ftruncate(iFd
,To
) != 0)
1350 return _error
->Error("Unable to truncate to %llu",To
);
1356 // FileFd::Tell - Current seek position /*{{{*/
1357 // ---------------------------------------------------------------------
1359 unsigned long long FileFd::Tell()
1361 // In theory, we could just return seekpos here always instead of
1362 // seeking around, but not all users of FileFd use always Seek() and co
1363 // so d->seekpos isn't always true and we can just use it as a hint if
1364 // we have nothing else, but not always as an authority…
1365 if (d
->pipe
== true)
1371 Res
= gztell(d
->gz
);
1374 Res
= lseek(iFd
,0,SEEK_CUR
);
1375 if (Res
== (off_t
)-1)
1376 _error
->Errno("lseek","Failed to determine the current file position");
1381 // FileFd::FileSize - Return the size of the file /*{{{*/
1382 // ---------------------------------------------------------------------
1384 unsigned long long FileFd::FileSize()
1387 if (d
->pipe
== false && fstat(iFd
,&Buf
) != 0)
1388 return _error
->Errno("fstat","Unable to determine the file size");
1390 // for compressor pipes st_size is undefined and at 'best' zero
1391 if (d
->pipe
== true || S_ISFIFO(Buf
.st_mode
))
1393 // we set it here, too, as we get the info here for free
1394 // in theory the Open-methods should take care of it already
1396 if (stat(FileName
.c_str(), &Buf
) != 0)
1397 return _error
->Errno("stat","Unable to determine the file size");
1403 // FileFd::Size - Return the size of the content in the file /*{{{*/
1404 // ---------------------------------------------------------------------
1406 unsigned long long FileFd::Size()
1408 unsigned long long size
= FileSize();
1410 // for compressor pipes st_size is undefined and at 'best' zero,
1411 // so we 'read' the content and 'seek' back - see there
1412 if (d
->pipe
== true)
1414 unsigned long long const oldSeek
= Tell();
1416 unsigned long long read
= 0;
1418 Read(ignore
, sizeof(ignore
), &read
);
1424 // only check gzsize if we are actually a gzip file, just checking for
1425 // "gz" is not sufficient as uncompressed files could be opened with
1426 // gzopen in "direct" mode as well
1427 else if (d
->gz
&& !gzdirect(d
->gz
) && size
> 0)
1429 off_t
const oldPos
= lseek(iFd
,0,SEEK_CUR
);
1430 /* unfortunately zlib.h doesn't provide a gzsize(), so we have to do
1431 * this ourselves; the original (uncompressed) file size is the last 32
1432 * bits of the file */
1433 // FIXME: Size for gz-files is limited by 32bit… no largefile support
1434 if (lseek(iFd
, -4, SEEK_END
) < 0)
1435 return _error
->Errno("lseek","Unable to seek to end of gzipped file");
1437 if (read(iFd
, &size
, 4) != 4)
1438 return _error
->Errno("read","Unable to read original size of gzipped file");
1440 #ifdef WORDS_BIGENDIAN
1441 uint32_t tmp_size
= size
;
1442 uint8_t const * const p
= (uint8_t const * const) &tmp_size
;
1443 tmp_size
= (p
[3] << 24) | (p
[2] << 16) | (p
[1] << 8) | p
[0];
1447 if (lseek(iFd
, oldPos
, SEEK_SET
) < 0)
1448 return _error
->Errno("lseek","Unable to seek in gzipped file");
1457 // FileFd::ModificationTime - Return the time of last touch /*{{{*/
1458 // ---------------------------------------------------------------------
1460 time_t FileFd::ModificationTime()
1463 if (d
->pipe
== false && fstat(iFd
,&Buf
) != 0)
1465 _error
->Errno("fstat","Unable to determine the modification time of file %s", FileName
.c_str());
1469 // for compressor pipes st_size is undefined and at 'best' zero
1470 if (d
->pipe
== true || S_ISFIFO(Buf
.st_mode
))
1472 // we set it here, too, as we get the info here for free
1473 // in theory the Open-methods should take care of it already
1475 if (stat(FileName
.c_str(), &Buf
) != 0)
1477 _error
->Errno("fstat","Unable to determine the modification time of file %s", FileName
.c_str());
1482 return Buf
.st_mtime
;
1485 // FileFd::Close - Close the file if the close flag is set /*{{{*/
1486 // ---------------------------------------------------------------------
1488 bool FileFd::Close()
1494 if ((Flags
& AutoClose
) == AutoClose
)
1497 if (d
!= NULL
&& d
->gz
!= NULL
) {
1498 int const e
= gzclose(d
->gz
);
1499 // gzdclose() on empty files always fails with "buffer error" here, ignore that
1500 if (e
!= 0 && e
!= Z_BUF_ERROR
)
1501 Res
&= _error
->Errno("close",_("Problem closing the gzip file %s"), FileName
.c_str());
1504 if (iFd
> 0 && close(iFd
) != 0)
1505 Res
&= _error
->Errno("close",_("Problem closing the file %s"), FileName
.c_str());
1508 if ((Flags
& Replace
) == Replace
&& iFd
>= 0) {
1509 if (rename(TemporaryFileName
.c_str(), FileName
.c_str()) != 0)
1510 Res
&= _error
->Errno("rename",_("Problem renaming the file %s to %s"), TemporaryFileName
.c_str(), FileName
.c_str());
1512 FileName
= TemporaryFileName
; // for the unlink() below.
1513 TemporaryFileName
.clear();
1518 if ((Flags
& Fail
) == Fail
&& (Flags
& DelOnFail
) == DelOnFail
&&
1519 FileName
.empty() == false)
1520 if (unlink(FileName
.c_str()) != 0)
1521 Res
&= _error
->WarningE("unlnk",_("Problem unlinking the file %s"), FileName
.c_str());
1525 if (d
->compressor_pid
> 0)
1526 ExecWait(d
->compressor_pid
, "FileFdCompressor", true);
1534 // FileFd::Sync - Sync the file /*{{{*/
1535 // ---------------------------------------------------------------------
1539 #ifdef _POSIX_SYNCHRONIZED_IO
1540 if (fsync(iFd
) != 0)
1541 return _error
->Errno("sync",_("Problem syncing the file"));
1547 gzFile
FileFd::gzFd() { return (gzFile
) d
->gz
; }