1 // -*- mode: cpp; mode: fold -*-
3 /* ######################################################################
7 CopyFile - Buffered copy of a single file
8 GetLock - dpkg compatible lock file manipulation (fcntl)
10 Most of this source is placed in the Public Domain, do with it what
12 It was originally written by Jason Gunthorpe <jgg@debian.org>.
13 FileFd gzip support added by Martin Pitt <martin.pitt@canonical.com>
15 The exception is RunScripts() it is under the GPLv2
17 ##################################################################### */
19 // Include Files /*{{{*/
22 #include <apt-pkg/fileutl.h>
23 #include <apt-pkg/strutl.h>
24 #include <apt-pkg/error.h>
25 #include <apt-pkg/sptr.h>
26 #include <apt-pkg/aptconfiguration.h>
27 #include <apt-pkg/configuration.h>
28 #include <apt-pkg/macros.h>
33 #include <sys/select.h>
65 #ifdef WORDS_BIGENDIAN
74 // RunScripts - Run a set of scripts from a configuration subtree /*{{{*/
75 // ---------------------------------------------------------------------
77 bool RunScripts(const char *Cnf
)
79 Configuration::Item
const *Opts
= _config
->Tree(Cnf
);
80 if (Opts
== 0 || Opts
->Child
== 0)
84 // Fork for running the system calls
85 pid_t Child
= ExecFork();
90 if (_config
->FindDir("DPkg::Chroot-Directory","/") != "/")
92 std::cerr
<< "Chrooting into "
93 << _config
->FindDir("DPkg::Chroot-Directory")
95 if (chroot(_config
->FindDir("DPkg::Chroot-Directory","/").c_str()) != 0)
99 if (chdir("/tmp/") != 0)
102 unsigned int Count
= 1;
103 for (; Opts
!= 0; Opts
= Opts
->Next
, Count
++)
105 if (Opts
->Value
.empty() == true)
108 if (system(Opts
->Value
.c_str()) != 0)
114 // Wait for the child
116 while (waitpid(Child
,&Status
,0) != Child
)
120 return _error
->Errno("waitpid","Couldn't wait for subprocess");
123 // Restore sig int/quit
124 signal(SIGQUIT
,SIG_DFL
);
125 signal(SIGINT
,SIG_DFL
);
127 // Check for an error code.
128 if (WIFEXITED(Status
) == 0 || WEXITSTATUS(Status
) != 0)
130 unsigned int Count
= WEXITSTATUS(Status
);
134 for (; Opts
!= 0 && Count
!= 1; Opts
= Opts
->Next
, Count
--);
135 _error
->Error("Problem executing scripts %s '%s'",Cnf
,Opts
->Value
.c_str());
138 return _error
->Error("Sub-process returned an error code");
145 // CopyFile - Buffered copy of a file /*{{{*/
146 // ---------------------------------------------------------------------
147 /* The caller is expected to set things so that failure causes erasure */
148 bool CopyFile(FileFd
&From
,FileFd
&To
)
150 if (From
.IsOpen() == false || To
.IsOpen() == false ||
151 From
.Failed() == true || To
.Failed() == true)
154 // Buffered copy between fds
155 SPtrArray
<unsigned char> Buf
= new unsigned char[64000];
156 unsigned long long Size
= From
.Size();
159 unsigned long long ToRead
= Size
;
163 if (From
.Read(Buf
,ToRead
) == false ||
164 To
.Write(Buf
,ToRead
) == false)
173 // GetLock - Gets a lock file /*{{{*/
174 // ---------------------------------------------------------------------
175 /* This will create an empty file of the given name and lock it. Once this
176 is done all other calls to GetLock in any other process will fail with
177 -1. The return result is the fd of the file, the call should call
178 close at some time. */
179 int GetLock(string File
,bool Errors
)
181 // GetLock() is used in aptitude on directories with public-write access
182 // Use O_NOFOLLOW here to prevent symlink traversal attacks
183 int FD
= open(File
.c_str(),O_RDWR
| O_CREAT
| O_NOFOLLOW
,0640);
186 // Read only .. can't have locking problems there.
189 _error
->Warning(_("Not using locking for read only lock file %s"),File
.c_str());
190 return dup(0); // Need something for the caller to close
194 _error
->Errno("open",_("Could not open lock file %s"),File
.c_str());
196 // Feh.. We do this to distinguish the lock vs open case..
200 SetCloseExec(FD
,true);
202 // Acquire a write lock
205 fl
.l_whence
= SEEK_SET
;
208 if (fcntl(FD
,F_SETLK
,&fl
) == -1)
210 // always close to not leak resources
217 _error
->Warning(_("Not using locking for nfs mounted lock file %s"),File
.c_str());
218 return dup(0); // Need something for the caller to close
222 _error
->Errno("open",_("Could not get lock %s"),File
.c_str());
230 // FileExists - Check if a file exists /*{{{*/
231 // ---------------------------------------------------------------------
232 /* Beware: Directories are also files! */
233 bool FileExists(string File
)
236 if (stat(File
.c_str(),&Buf
) != 0)
241 // RealFileExists - Check if a file exists and if it is really a file /*{{{*/
242 // ---------------------------------------------------------------------
244 bool RealFileExists(string File
)
247 if (stat(File
.c_str(),&Buf
) != 0)
249 return ((Buf
.st_mode
& S_IFREG
) != 0);
252 // DirectoryExists - Check if a directory exists and is really one /*{{{*/
253 // ---------------------------------------------------------------------
255 bool DirectoryExists(string
const &Path
)
258 if (stat(Path
.c_str(),&Buf
) != 0)
260 return ((Buf
.st_mode
& S_IFDIR
) != 0);
263 // CreateDirectory - poor man's mkdir -p guarded by a parent directory /*{{{*/
264 // ---------------------------------------------------------------------
265 /* This method will create all directories needed for path in good old
266 mkdir -p style but refuses to do this if Parent is not a prefix of
267 this Path. Example: /var/cache/ and /var/cache/apt/archives are given,
268 so it will create apt/archives if /var/cache exists - on the other
269 hand if the parent is /var/lib the creation will fail as this path
270 is not a parent of the path to be generated. */
271 bool CreateDirectory(string
const &Parent
, string
const &Path
)
273 if (Parent
.empty() == true || Path
.empty() == true)
276 if (DirectoryExists(Path
) == true)
279 if (DirectoryExists(Parent
) == false)
282 // we are not going to create directories "into the blue"
283 if (Path
.compare(0, Parent
.length(), Parent
) != 0)
286 vector
<string
> const dirs
= VectorizeString(Path
.substr(Parent
.size()), '/');
287 string progress
= Parent
;
288 for (vector
<string
>::const_iterator d
= dirs
.begin(); d
!= dirs
.end(); ++d
)
290 if (d
->empty() == true)
293 progress
.append("/").append(*d
);
294 if (DirectoryExists(progress
) == true)
297 if (mkdir(progress
.c_str(), 0755) != 0)
303 // CreateAPTDirectoryIfNeeded - ensure that the given directory exists /*{{{*/
304 // ---------------------------------------------------------------------
305 /* a small wrapper around CreateDirectory to check if it exists and to
306 remove the trailing "/apt/" from the parent directory if needed */
307 bool CreateAPTDirectoryIfNeeded(string
const &Parent
, string
const &Path
)
309 if (DirectoryExists(Path
) == true)
312 size_t const len
= Parent
.size();
313 if (len
> 5 && Parent
.find("/apt/", len
- 6, 5) == len
- 5)
315 if (CreateDirectory(Parent
.substr(0,len
-5), Path
) == true)
318 else if (CreateDirectory(Parent
, Path
) == true)
324 // GetListOfFilesInDir - returns a vector of files in the given dir /*{{{*/
325 // ---------------------------------------------------------------------
326 /* If an extension is given only files with this extension are included
327 in the returned vector, otherwise every "normal" file is included. */
328 std::vector
<string
> GetListOfFilesInDir(string
const &Dir
, string
const &Ext
,
329 bool const &SortList
, bool const &AllowNoExt
)
331 std::vector
<string
> ext
;
333 if (Ext
.empty() == false)
335 if (AllowNoExt
== true && ext
.empty() == false)
337 return GetListOfFilesInDir(Dir
, ext
, SortList
);
339 std::vector
<string
> GetListOfFilesInDir(string
const &Dir
, std::vector
<string
> const &Ext
,
340 bool const &SortList
)
342 // Attention debuggers: need to be set with the environment config file!
343 bool const Debug
= _config
->FindB("Debug::GetListOfFilesInDir", false);
346 std::clog
<< "Accept in " << Dir
<< " only files with the following " << Ext
.size() << " extensions:" << std::endl
;
347 if (Ext
.empty() == true)
348 std::clog
<< "\tNO extension" << std::endl
;
350 for (std::vector
<string
>::const_iterator e
= Ext
.begin();
352 std::clog
<< '\t' << (e
->empty() == true ? "NO" : *e
) << " extension" << std::endl
;
355 std::vector
<string
> List
;
357 if (DirectoryExists(Dir
) == false)
359 _error
->Error(_("List of files can't be created as '%s' is not a directory"), Dir
.c_str());
363 Configuration::MatchAgainstConfig
SilentIgnore("Dir::Ignore-Files-Silently");
364 DIR *D
= opendir(Dir
.c_str());
367 _error
->Errno("opendir",_("Unable to read %s"),Dir
.c_str());
371 for (struct dirent
*Ent
= readdir(D
); Ent
!= 0; Ent
= readdir(D
))
373 // skip "hidden" files
374 if (Ent
->d_name
[0] == '.')
377 // Make sure it is a file and not something else
378 string
const File
= flCombine(Dir
,Ent
->d_name
);
379 #ifdef _DIRENT_HAVE_D_TYPE
380 if (Ent
->d_type
!= DT_REG
)
383 if (RealFileExists(File
) == false)
385 // do not show ignoration warnings for directories
387 #ifdef _DIRENT_HAVE_D_TYPE
388 Ent
->d_type
== DT_DIR
||
390 DirectoryExists(File
) == true)
392 if (SilentIgnore
.Match(Ent
->d_name
) == false)
393 _error
->Notice(_("Ignoring '%s' in directory '%s' as it is not a regular file"), Ent
->d_name
, Dir
.c_str());
398 // check for accepted extension:
399 // no extension given -> periods are bad as hell!
400 // extensions given -> "" extension allows no extension
401 if (Ext
.empty() == false)
403 string d_ext
= flExtension(Ent
->d_name
);
404 if (d_ext
== Ent
->d_name
) // no extension
406 if (std::find(Ext
.begin(), Ext
.end(), "") == Ext
.end())
409 std::clog
<< "Bad file: " << Ent
->d_name
<< " → no extension" << std::endl
;
410 if (SilentIgnore
.Match(Ent
->d_name
) == false)
411 _error
->Notice(_("Ignoring file '%s' in directory '%s' as it has no filename extension"), Ent
->d_name
, Dir
.c_str());
415 else if (std::find(Ext
.begin(), Ext
.end(), d_ext
) == Ext
.end())
418 std::clog
<< "Bad file: " << Ent
->d_name
<< " → bad extension »" << flExtension(Ent
->d_name
) << "«" << std::endl
;
419 if (SilentIgnore
.Match(Ent
->d_name
) == false)
420 _error
->Notice(_("Ignoring file '%s' in directory '%s' as it has an invalid filename extension"), Ent
->d_name
, Dir
.c_str());
425 // Skip bad filenames ala run-parts
426 const char *C
= Ent
->d_name
;
428 if (isalpha(*C
) == 0 && isdigit(*C
) == 0
429 && *C
!= '_' && *C
!= '-' && *C
!= ':') {
430 // no required extension -> dot is a bad character
431 if (*C
== '.' && Ext
.empty() == false)
436 // we don't reach the end of the name -> bad character included
440 std::clog
<< "Bad file: " << Ent
->d_name
<< " → bad character »"
441 << *C
<< "« in filename (period allowed: " << (Ext
.empty() ? "no" : "yes") << ")" << std::endl
;
445 // skip filenames which end with a period. These are never valid
449 std::clog
<< "Bad file: " << Ent
->d_name
<< " → Period as last character" << std::endl
;
454 std::clog
<< "Accept file: " << Ent
->d_name
<< " in " << Dir
<< std::endl
;
455 List
.push_back(File
);
459 if (SortList
== true)
460 std::sort(List
.begin(),List
.end());
463 std::vector
<string
> GetListOfFilesInDir(string
const &Dir
, bool SortList
)
465 bool const Debug
= _config
->FindB("Debug::GetListOfFilesInDir", false);
467 std::clog
<< "Accept in " << Dir
<< " all regular files" << std::endl
;
469 std::vector
<string
> List
;
471 if (DirectoryExists(Dir
) == false)
473 _error
->Error(_("List of files can't be created as '%s' is not a directory"), Dir
.c_str());
477 DIR *D
= opendir(Dir
.c_str());
480 _error
->Errno("opendir",_("Unable to read %s"),Dir
.c_str());
484 for (struct dirent
*Ent
= readdir(D
); Ent
!= 0; Ent
= readdir(D
))
486 // skip "hidden" files
487 if (Ent
->d_name
[0] == '.')
490 // Make sure it is a file and not something else
491 string
const File
= flCombine(Dir
,Ent
->d_name
);
492 #ifdef _DIRENT_HAVE_D_TYPE
493 if (Ent
->d_type
!= DT_REG
)
496 if (RealFileExists(File
) == false)
499 std::clog
<< "Bad file: " << Ent
->d_name
<< " → it is not a real file" << std::endl
;
504 // Skip bad filenames ala run-parts
505 const char *C
= Ent
->d_name
;
507 if (isalpha(*C
) == 0 && isdigit(*C
) == 0
508 && *C
!= '_' && *C
!= '-' && *C
!= '.')
511 // we don't reach the end of the name -> bad character included
515 std::clog
<< "Bad file: " << Ent
->d_name
<< " → bad character »" << *C
<< "« in filename" << std::endl
;
519 // skip filenames which end with a period. These are never valid
523 std::clog
<< "Bad file: " << Ent
->d_name
<< " → Period as last character" << std::endl
;
528 std::clog
<< "Accept file: " << Ent
->d_name
<< " in " << Dir
<< std::endl
;
529 List
.push_back(File
);
533 if (SortList
== true)
534 std::sort(List
.begin(),List
.end());
538 // SafeGetCWD - This is a safer getcwd that returns a dynamic string /*{{{*/
539 // ---------------------------------------------------------------------
540 /* We return / on failure. */
543 // Stash the current dir.
546 if (getcwd(S
,sizeof(S
)-2) == 0)
548 unsigned int Len
= strlen(S
);
554 // GetModificationTime - Get the mtime of the given file or -1 on error /*{{{*/
555 // ---------------------------------------------------------------------
556 /* We return / on failure. */
557 time_t GetModificationTime(string
const &Path
)
560 if (stat(Path
.c_str(), &St
) < 0)
565 // flNotDir - Strip the directory from the filename /*{{{*/
566 // ---------------------------------------------------------------------
568 string
flNotDir(string File
)
570 string::size_type Res
= File
.rfind('/');
571 if (Res
== string::npos
)
574 return string(File
,Res
,Res
- File
.length());
577 // flNotFile - Strip the file from the directory name /*{{{*/
578 // ---------------------------------------------------------------------
579 /* Result ends in a / */
580 string
flNotFile(string File
)
582 string::size_type Res
= File
.rfind('/');
583 if (Res
== string::npos
)
586 return string(File
,0,Res
);
589 // flExtension - Return the extension for the file /*{{{*/
590 // ---------------------------------------------------------------------
592 string
flExtension(string File
)
594 string::size_type Res
= File
.rfind('.');
595 if (Res
== string::npos
)
598 return string(File
,Res
,Res
- File
.length());
601 // flNoLink - If file is a symlink then deref it /*{{{*/
602 // ---------------------------------------------------------------------
603 /* If the name is not a link then the returned path is the input. */
604 string
flNoLink(string File
)
607 if (lstat(File
.c_str(),&St
) != 0 || S_ISLNK(St
.st_mode
) == 0)
609 if (stat(File
.c_str(),&St
) != 0)
612 /* Loop resolving the link. There is no need to limit the number of
613 loops because the stat call above ensures that the symlink is not
621 if ((Res
= readlink(NFile
.c_str(),Buffer
,sizeof(Buffer
))) <= 0 ||
622 (size_t)Res
>= sizeof(Buffer
))
625 // Append or replace the previous path
627 if (Buffer
[0] == '/')
630 NFile
= flNotFile(NFile
) + Buffer
;
632 // See if we are done
633 if (lstat(NFile
.c_str(),&St
) != 0)
635 if (S_ISLNK(St
.st_mode
) == 0)
640 // flCombine - Combine a file and a directory /*{{{*/
641 // ---------------------------------------------------------------------
642 /* If the file is an absolute path then it is just returned, otherwise
643 the directory is pre-pended to it. */
644 string
flCombine(string Dir
,string File
)
646 if (File
.empty() == true)
649 if (File
[0] == '/' || Dir
.empty() == true)
651 if (File
.length() >= 2 && File
[0] == '.' && File
[1] == '/')
653 if (Dir
[Dir
.length()-1] == '/')
655 return Dir
+ '/' + File
;
658 // SetCloseExec - Set the close on exec flag /*{{{*/
659 // ---------------------------------------------------------------------
661 void SetCloseExec(int Fd
,bool Close
)
663 if (fcntl(Fd
,F_SETFD
,(Close
== false)?0:FD_CLOEXEC
) != 0)
665 cerr
<< "FATAL -> Could not set close on exec " << strerror(errno
) << endl
;
670 // SetNonBlock - Set the nonblocking flag /*{{{*/
671 // ---------------------------------------------------------------------
673 void SetNonBlock(int Fd
,bool Block
)
675 int Flags
= fcntl(Fd
,F_GETFL
) & (~O_NONBLOCK
);
676 if (fcntl(Fd
,F_SETFL
,Flags
| ((Block
== false)?0:O_NONBLOCK
)) != 0)
678 cerr
<< "FATAL -> Could not set non-blocking flag " << strerror(errno
) << endl
;
683 // WaitFd - Wait for a FD to become readable /*{{{*/
684 // ---------------------------------------------------------------------
685 /* This waits for a FD to become readable using select. It is useful for
686 applications making use of non-blocking sockets. The timeout is
688 bool WaitFd(int Fd
,bool write
,unsigned long timeout
)
701 Res
= select(Fd
+1,0,&Set
,0,(timeout
!= 0?&tv
:0));
703 while (Res
< 0 && errno
== EINTR
);
713 Res
= select(Fd
+1,&Set
,0,0,(timeout
!= 0?&tv
:0));
715 while (Res
< 0 && errno
== EINTR
);
724 // MergeKeepFdsFromConfiguration - Merge APT::Keep-Fds configuration /*{{{*/
725 // ---------------------------------------------------------------------
726 /* This is used to merge the APT::Keep-Fds with the provided KeepFDs
729 void MergeKeepFdsFromConfiguration(std::set
<int> &KeepFDs
)
731 Configuration::Item
const *Opts
= _config
->Tree("APT::Keep-Fds");
732 if (Opts
!= 0 && Opts
->Child
!= 0)
735 for (; Opts
!= 0; Opts
= Opts
->Next
)
737 if (Opts
->Value
.empty() == true)
739 int fd
= atoi(Opts
->Value
.c_str());
745 // ExecFork - Magical fork that sanitizes the context before execing /*{{{*/
746 // ---------------------------------------------------------------------
747 /* This is used if you want to cleanse the environment for the forked
748 child, it fixes up the important signals and nukes all of the fds,
749 otherwise acts like normal fork. */
753 // we need to merge the Keep-Fds as external tools like
754 // debconf-apt-progress use it
755 MergeKeepFdsFromConfiguration(KeepFDs
);
756 return ExecFork(KeepFDs
);
759 pid_t
ExecFork(std::set
<int> KeepFDs
)
761 // Fork off the process
762 pid_t Process
= fork();
765 cerr
<< "FATAL -> Failed to fork." << endl
;
769 // Spawn the subprocess
773 signal(SIGPIPE
,SIG_DFL
);
774 signal(SIGQUIT
,SIG_DFL
);
775 signal(SIGINT
,SIG_DFL
);
776 signal(SIGWINCH
,SIG_DFL
);
777 signal(SIGCONT
,SIG_DFL
);
778 signal(SIGTSTP
,SIG_DFL
);
780 // Close all of our FDs - just in case
781 for (int K
= 3; K
!= sysconf(_SC_OPEN_MAX
); K
++)
783 if(KeepFDs
.find(K
) == KeepFDs
.end())
784 fcntl(K
,F_SETFD
,FD_CLOEXEC
);
791 // ExecWait - Fancy waitpid /*{{{*/
792 // ---------------------------------------------------------------------
793 /* Waits for the given sub process. If Reap is set then no errors are
794 generated. Otherwise a failed subprocess will generate a proper descriptive
796 bool ExecWait(pid_t Pid
,const char *Name
,bool Reap
)
801 // Wait and collect the error code
803 while (waitpid(Pid
,&Status
,0) != Pid
)
811 return _error
->Error(_("Waited for %s but it wasn't there"),Name
);
815 // Check for an error code.
816 if (WIFEXITED(Status
) == 0 || WEXITSTATUS(Status
) != 0)
820 if (WIFSIGNALED(Status
) != 0)
822 if( WTERMSIG(Status
) == SIGSEGV
)
823 return _error
->Error(_("Sub-process %s received a segmentation fault."),Name
);
825 return _error
->Error(_("Sub-process %s received signal %u."),Name
, WTERMSIG(Status
));
828 if (WIFEXITED(Status
) != 0)
829 return _error
->Error(_("Sub-process %s returned an error code (%u)"),Name
,WEXITSTATUS(Status
));
831 return _error
->Error(_("Sub-process %s exited unexpectedly"),Name
);
838 class FileFdPrivate
{ /*{{{*/
849 uint8_t buffer
[4096];
855 LZMAFILE() : file(NULL
), eof(false), compressing(false) {}
857 if (compressing
== true)
860 stream
.avail_out
= sizeof(buffer
)/sizeof(buffer
[0]);
861 stream
.next_out
= buffer
;
862 err
= lzma_code(&stream
, LZMA_FINISH
);
863 if (err
!= LZMA_OK
&& err
!= LZMA_STREAM_END
)
865 _error
->Error("~LZMAFILE: Compress finalisation failed");
868 size_t const n
= sizeof(buffer
)/sizeof(buffer
[0]) - stream
.avail_out
;
869 if (n
&& fwrite(buffer
, 1, n
, file
) != n
)
871 _error
->Errno("~LZMAFILE",_("Write error"));
874 if (err
== LZMA_STREAM_END
)
885 pid_t compressor_pid
;
887 APT::Configuration::Compressor compressor
;
888 unsigned int openmode
;
889 unsigned long long seekpos
;
900 compressed_fd(-1), compressor_pid(-1), pipe(false),
901 openmode(0), seekpos(0) {};
902 bool InternalClose(std::string
const &FileName
)
905 /* dummy so that the rest can be 'else if's */;
907 else if (gz
!= NULL
) {
908 int const e
= gzclose(gz
);
910 // gzdclose() on empty files always fails with "buffer error" here, ignore that
911 if (e
!= 0 && e
!= Z_BUF_ERROR
)
912 return _error
->Errno("close",_("Problem closing the gzip file %s"), FileName
.c_str());
916 else if (bz2
!= NULL
) {
922 else if (lzma
!= NULL
) {
929 bool CloseDown(std::string
const &FileName
)
931 bool const Res
= InternalClose(FileName
);
933 if (compressor_pid
> 0)
934 ExecWait(compressor_pid
, "FileFdCompressor", true);
939 bool InternalStream() const {
951 ~FileFdPrivate() { CloseDown(""); }
954 // FileFd::Open - Open a file /*{{{*/
955 // ---------------------------------------------------------------------
956 /* The most commonly used open mode combinations are given with Mode */
957 bool FileFd::Open(string FileName
,unsigned int const Mode
,CompressMode Compress
, unsigned long const Perms
)
959 if (Mode
== ReadOnlyGzip
)
960 return Open(FileName
, ReadOnly
, Gzip
, Perms
);
962 if (Compress
== Auto
&& (Mode
& WriteOnly
) == WriteOnly
)
963 return FileFdError("Autodetection on %s only works in ReadOnly openmode!", FileName
.c_str());
965 std::vector
<APT::Configuration::Compressor
> const compressors
= APT::Configuration::getCompressors();
966 std::vector
<APT::Configuration::Compressor
>::const_iterator compressor
= compressors
.begin();
967 if (Compress
== Auto
)
969 for (; compressor
!= compressors
.end(); ++compressor
)
971 std::string file
= FileName
+ compressor
->Extension
;
972 if (FileExists(file
) == false)
978 else if (Compress
== Extension
)
980 std::string::size_type
const found
= FileName
.find_last_of('.');
982 if (found
!= std::string::npos
)
984 ext
= FileName
.substr(found
);
985 if (ext
== ".new" || ext
== ".bak")
987 std::string::size_type
const found2
= FileName
.find_last_of('.', found
- 1);
988 if (found2
!= std::string::npos
)
989 ext
= FileName
.substr(found2
, found
- found2
);
994 for (; compressor
!= compressors
.end(); ++compressor
)
995 if (ext
== compressor
->Extension
)
997 // no matching extension - assume uncompressed (imagine files like 'example.org_Packages')
998 if (compressor
== compressors
.end())
999 for (compressor
= compressors
.begin(); compressor
!= compressors
.end(); ++compressor
)
1000 if (compressor
->Name
== ".")
1008 case None
: name
= "."; break;
1009 case Gzip
: name
= "gzip"; break;
1010 case Bzip2
: name
= "bzip2"; break;
1011 case Lzma
: name
= "lzma"; break;
1012 case Xz
: name
= "xz"; break;
1016 return FileFdError("Opening File %s in None, Auto or Extension should be already handled?!?", FileName
.c_str());
1018 for (; compressor
!= compressors
.end(); ++compressor
)
1019 if (compressor
->Name
== name
)
1021 if (compressor
== compressors
.end())
1022 return FileFdError("Can't find a configured compressor %s for file %s", name
.c_str(), FileName
.c_str());
1025 if (compressor
== compressors
.end())
1026 return FileFdError("Can't find a match for specified compressor mode for file %s", FileName
.c_str());
1027 return Open(FileName
, Mode
, *compressor
, Perms
);
1029 bool FileFd::Open(string FileName
,unsigned int const Mode
,APT::Configuration::Compressor
const &compressor
, unsigned long const Perms
)
1034 if ((Mode
& WriteOnly
) != WriteOnly
&& (Mode
& (Atomic
| Create
| Empty
| Exclusive
)) != 0)
1035 return FileFdError("ReadOnly mode for %s doesn't accept additional flags!", FileName
.c_str());
1036 if ((Mode
& ReadWrite
) == 0)
1037 return FileFdError("No openmode provided in FileFd::Open for %s", FileName
.c_str());
1039 if ((Mode
& Atomic
) == Atomic
)
1043 else if ((Mode
& (Exclusive
| Create
)) == (Exclusive
| Create
))
1045 // for atomic, this will be done by rename in Close()
1046 unlink(FileName
.c_str());
1048 if ((Mode
& Empty
) == Empty
)
1051 if (lstat(FileName
.c_str(),&Buf
) == 0 && S_ISLNK(Buf
.st_mode
))
1052 unlink(FileName
.c_str());
1056 #define if_FLAGGED_SET(FLAG, MODE) if ((Mode & FLAG) == FLAG) fileflags |= MODE
1057 if_FLAGGED_SET(ReadWrite
, O_RDWR
);
1058 else if_FLAGGED_SET(ReadOnly
, O_RDONLY
);
1059 else if_FLAGGED_SET(WriteOnly
, O_WRONLY
);
1061 if_FLAGGED_SET(Create
, O_CREAT
);
1062 if_FLAGGED_SET(Empty
, O_TRUNC
);
1063 if_FLAGGED_SET(Exclusive
, O_EXCL
);
1064 #undef if_FLAGGED_SET
1066 if ((Mode
& Atomic
) == Atomic
)
1068 char *name
= strdup((FileName
+ ".XXXXXX").c_str());
1070 if((iFd
= mkstemp(name
)) == -1)
1073 return FileFdErrno("mkstemp", "Could not create temporary file for %s", FileName
.c_str());
1076 TemporaryFileName
= string(name
);
1079 if(Perms
!= 600 && fchmod(iFd
, Perms
) == -1)
1080 return FileFdErrno("fchmod", "Could not change permissions for temporary file %s", TemporaryFileName
.c_str());
1083 iFd
= open(FileName
.c_str(), fileflags
, Perms
);
1085 this->FileName
= FileName
;
1086 if (iFd
== -1 || OpenInternDescriptor(Mode
, compressor
) == false)
1093 return FileFdErrno("open",_("Could not open file %s"), FileName
.c_str());
1096 SetCloseExec(iFd
,true);
1100 // FileFd::OpenDescriptor - Open a filedescriptor /*{{{*/
1101 // ---------------------------------------------------------------------
1103 bool FileFd::OpenDescriptor(int Fd
, unsigned int const Mode
, CompressMode Compress
, bool AutoClose
)
1105 std::vector
<APT::Configuration::Compressor
> const compressors
= APT::Configuration::getCompressors();
1106 std::vector
<APT::Configuration::Compressor
>::const_iterator compressor
= compressors
.begin();
1109 // compat with the old API
1110 if (Mode
== ReadOnlyGzip
&& Compress
== None
)
1115 case None
: name
= "."; break;
1116 case Gzip
: name
= "gzip"; break;
1117 case Bzip2
: name
= "bzip2"; break;
1118 case Lzma
: name
= "lzma"; break;
1119 case Xz
: name
= "xz"; break;
1122 if (AutoClose
== true && Fd
!= -1)
1124 return FileFdError("Opening Fd %d in Auto or Extension compression mode is not supported", Fd
);
1126 for (; compressor
!= compressors
.end(); ++compressor
)
1127 if (compressor
->Name
== name
)
1129 if (compressor
== compressors
.end())
1131 if (AutoClose
== true && Fd
!= -1)
1133 return FileFdError("Can't find a configured compressor %s for file %s", name
.c_str(), FileName
.c_str());
1135 return OpenDescriptor(Fd
, Mode
, *compressor
, AutoClose
);
1137 bool FileFd::OpenDescriptor(int Fd
, unsigned int const Mode
, APT::Configuration::Compressor
const &compressor
, bool AutoClose
)
1140 Flags
= (AutoClose
) ? FileFd::AutoClose
: 0;
1142 this->FileName
= "";
1143 if (OpenInternDescriptor(Mode
, compressor
) == false)
1146 (Flags
& Compressed
) == Compressed
||
1152 return FileFdError(_("Could not open file descriptor %d"), Fd
);
1156 bool FileFd::OpenInternDescriptor(unsigned int const Mode
, APT::Configuration::Compressor
const &compressor
)
1160 if (compressor
.Name
== "." || compressor
.Binary
.empty() == true)
1163 #if defined HAVE_ZLIB || defined HAVE_BZ2 || defined HAVE_LZMA
1164 // the API to open files is similar, so setup to avoid code duplicates later
1165 // and while at it ensure that we close before opening (if its a reopen)
1166 void* (*compress_open
)(int, const char *) = NULL
;
1168 /* dummy so that the rest can be 'else if's */;
1169 #define APT_COMPRESS_INIT(NAME,OPEN) \
1170 else if (compressor.Name == NAME) \
1172 compress_open = (void*(*)(int, const char *)) OPEN; \
1173 if (d != NULL) d->InternalClose(FileName); \
1176 APT_COMPRESS_INIT("gzip", gzdopen
)
1179 APT_COMPRESS_INIT("bzip2", BZ2_bzdopen
)
1182 APT_COMPRESS_INIT("xz", fdopen
)
1183 APT_COMPRESS_INIT("lzma", fdopen
)
1185 #undef APT_COMPRESS_INIT
1190 d
= new FileFdPrivate();
1192 d
->compressor
= compressor
;
1193 #if defined HAVE_ZLIB || defined HAVE_BZ2 || defined HAVE_LZMA
1194 if ((Flags
& AutoClose
) != AutoClose
&& compress_open
!= NULL
)
1196 // Need to duplicate fd here or gz/bz2 close for cleanup will close the fd as well
1197 int const internFd
= dup(iFd
);
1199 return FileFdErrno("OpenInternDescriptor", _("Could not open file descriptor %d"), iFd
);
1205 #if defined HAVE_ZLIB || defined HAVE_BZ2 || defined HAVE_LZMA
1206 if (compress_open
!= NULL
)
1208 void* compress_struct
= NULL
;
1209 if ((Mode
& ReadWrite
) == ReadWrite
)
1210 compress_struct
= compress_open(iFd
, "r+");
1211 else if ((Mode
& WriteOnly
) == WriteOnly
)
1212 compress_struct
= compress_open(iFd
, "w");
1214 compress_struct
= compress_open(iFd
, "r");
1215 if (compress_struct
== NULL
)
1219 /* dummy so that the rest can be 'else if's */;
1221 else if (compressor
.Name
== "gzip")
1222 d
->gz
= (gzFile
) compress_struct
;
1225 else if (compressor
.Name
== "bzip2")
1226 d
->bz2
= (BZFILE
*) compress_struct
;
1229 else if (compressor
.Name
== "xz" || compressor
.Name
== "lzma")
1231 uint32_t const xzlevel
= 6;
1232 uint64_t const memlimit
= UINT64_MAX
;
1233 if (d
->lzma
== NULL
)
1234 d
->lzma
= new FileFdPrivate::LZMAFILE
;
1235 d
->lzma
->file
= (FILE*) compress_struct
;
1236 d
->lzma
->stream
= LZMA_STREAM_INIT
;
1238 if ((Mode
& ReadWrite
) == ReadWrite
)
1239 return FileFdError("ReadWrite mode is not supported for file %s", FileName
.c_str());
1241 if ((Mode
& WriteOnly
) == WriteOnly
)
1243 if (compressor
.Name
== "xz")
1245 if (lzma_easy_encoder(&d
->lzma
->stream
, xzlevel
, LZMA_CHECK_CRC32
) != LZMA_OK
)
1250 lzma_options_lzma options
;
1251 lzma_lzma_preset(&options
, xzlevel
);
1252 if (lzma_alone_encoder(&d
->lzma
->stream
, &options
) != LZMA_OK
)
1255 d
->lzma
->compressing
= true;
1259 if (compressor
.Name
== "xz")
1261 if (lzma_auto_decoder(&d
->lzma
->stream
, memlimit
, 0) != LZMA_OK
)
1266 if (lzma_alone_decoder(&d
->lzma
->stream
, memlimit
) != LZMA_OK
)
1269 d
->lzma
->compressing
= false;
1273 Flags
|= Compressed
;
1278 // collect zombies here in case we reopen
1279 if (d
->compressor_pid
> 0)
1280 ExecWait(d
->compressor_pid
, "FileFdCompressor", true);
1282 if ((Mode
& ReadWrite
) == ReadWrite
)
1283 return FileFdError("ReadWrite mode is not supported for file %s", FileName
.c_str());
1285 bool const Comp
= (Mode
& WriteOnly
) == WriteOnly
;
1288 // Handle 'decompression' of empty files
1291 if (Buf
.st_size
== 0 && S_ISFIFO(Buf
.st_mode
) == false)
1294 // We don't need the file open - instead let the compressor open it
1295 // as he properly knows better how to efficiently read from 'his' file
1296 if (FileName
.empty() == false)
1303 // Create a data pipe
1304 int Pipe
[2] = {-1,-1};
1305 if (pipe(Pipe
) != 0)
1306 return FileFdErrno("pipe",_("Failed to create subprocess IPC"));
1307 for (int J
= 0; J
!= 2; J
++)
1308 SetCloseExec(Pipe
[J
],true);
1310 d
->compressed_fd
= iFd
;
1319 d
->compressor_pid
= ExecFork();
1320 if (d
->compressor_pid
== 0)
1324 dup2(d
->compressed_fd
,STDOUT_FILENO
);
1325 dup2(Pipe
[0],STDIN_FILENO
);
1329 if (d
->compressed_fd
!= -1)
1330 dup2(d
->compressed_fd
,STDIN_FILENO
);
1331 dup2(Pipe
[1],STDOUT_FILENO
);
1333 int const nullfd
= open("/dev/null", O_WRONLY
);
1336 dup2(nullfd
,STDERR_FILENO
);
1340 SetCloseExec(STDOUT_FILENO
,false);
1341 SetCloseExec(STDIN_FILENO
,false);
1343 std::vector
<char const*> Args
;
1344 Args
.push_back(compressor
.Binary
.c_str());
1345 std::vector
<std::string
> const * const addArgs
=
1346 (Comp
== true) ? &(compressor
.CompressArgs
) : &(compressor
.UncompressArgs
);
1347 for (std::vector
<std::string
>::const_iterator a
= addArgs
->begin();
1348 a
!= addArgs
->end(); ++a
)
1349 Args
.push_back(a
->c_str());
1350 if (Comp
== false && FileName
.empty() == false)
1352 Args
.push_back("--stdout");
1353 if (TemporaryFileName
.empty() == false)
1354 Args
.push_back(TemporaryFileName
.c_str());
1356 Args
.push_back(FileName
.c_str());
1358 Args
.push_back(NULL
);
1360 execvp(Args
[0],(char **)&Args
[0]);
1361 cerr
<< _("Failed to exec compressor ") << Args
[0] << endl
;
1372 // FileFd::~File - Closes the file /*{{{*/
1373 // ---------------------------------------------------------------------
1374 /* If the proper modes are selected then we close the Fd and possibly
1375 unlink the file on error. */
1380 d
->CloseDown(FileName
);
1385 // FileFd::Read - Read a bit of the file /*{{{*/
1386 // ---------------------------------------------------------------------
1387 /* We are careful to handle interruption by a signal while reading
1389 bool FileFd::Read(void *To
,unsigned long long Size
,unsigned long long *Actual
)
1395 *((char *)To
) = '\0';
1399 /* dummy so that the rest can be 'else if's */;
1401 else if (d
!= NULL
&& d
->gz
!= NULL
)
1402 Res
= gzread(d
->gz
,To
,Size
);
1405 else if (d
!= NULL
&& d
->bz2
!= NULL
)
1406 Res
= BZ2_bzread(d
->bz2
,To
,Size
);
1409 else if (d
!= NULL
&& d
->lzma
!= NULL
)
1411 if (d
->lzma
->eof
== true)
1414 d
->lzma
->stream
.next_out
= (uint8_t *) To
;
1415 d
->lzma
->stream
.avail_out
= Size
;
1416 if (d
->lzma
->stream
.avail_in
== 0)
1418 d
->lzma
->stream
.next_in
= d
->lzma
->buffer
;
1419 d
->lzma
->stream
.avail_in
= fread(d
->lzma
->buffer
, 1, sizeof(d
->lzma
->buffer
)/sizeof(d
->lzma
->buffer
[0]), d
->lzma
->file
);
1421 d
->lzma
->err
= lzma_code(&d
->lzma
->stream
, LZMA_RUN
);
1422 if (d
->lzma
->err
== LZMA_STREAM_END
)
1424 d
->lzma
->eof
= true;
1425 Res
= Size
- d
->lzma
->stream
.avail_out
;
1427 else if (d
->lzma
->err
!= LZMA_OK
)
1434 Res
= Size
- d
->lzma
->stream
.avail_out
;
1437 // lzma run was okay, but produced no output…
1445 Res
= read(iFd
,To
,Size
);
1451 // trick the while-loop into running again
1457 /* dummy so that the rest can be 'else if's */;
1459 else if (d
!= NULL
&& d
->gz
!= NULL
)
1462 char const * const errmsg
= gzerror(d
->gz
, &err
);
1464 return FileFdError("gzread: %s (%d: %s)", _("Read error"), err
, errmsg
);
1468 else if (d
!= NULL
&& d
->bz2
!= NULL
)
1471 char const * const errmsg
= BZ2_bzerror(d
->bz2
, &err
);
1472 if (err
!= BZ_IO_ERROR
)
1473 return FileFdError("BZ2_bzread: %s (%d: %s)", _("Read error"), err
, errmsg
);
1477 else if (d
!= NULL
&& d
->lzma
!= NULL
)
1478 return FileFdError("lzma_read: %s (%d)", _("Read error"), d
->lzma
->err
);
1480 return FileFdErrno("read",_("Read error"));
1483 To
= (char *)To
+ Res
;
1490 while (Res
> 0 && Size
> 0);
1502 return FileFdError(_("read, still have %llu to read but none left"), Size
);
1505 // FileFd::ReadLine - Read a complete line from the file /*{{{*/
1506 // ---------------------------------------------------------------------
1507 /* Beware: This method can be quiet slow for big buffers on UNcompressed
1508 files because of the naive implementation! */
1509 char* FileFd::ReadLine(char *To
, unsigned long long const Size
)
1513 if (d
!= NULL
&& d
->gz
!= NULL
)
1514 return gzgets(d
->gz
, To
, Size
);
1517 unsigned long long read
= 0;
1518 while ((Size
- 1) != read
)
1520 unsigned long long done
= 0;
1521 if (Read(To
+ read
, 1, &done
) == false)
1525 if (To
[read
++] == '\n')
1534 // FileFd::Write - Write to the file /*{{{*/
1535 // ---------------------------------------------------------------------
1537 bool FileFd::Write(const void *From
,unsigned long long Size
)
1544 /* dummy so that the rest can be 'else if's */;
1546 else if (d
!= NULL
&& d
->gz
!= NULL
)
1547 Res
= gzwrite(d
->gz
,From
,Size
);
1550 else if (d
!= NULL
&& d
->bz2
!= NULL
)
1551 Res
= BZ2_bzwrite(d
->bz2
,(void*)From
,Size
);
1554 else if (d
!= NULL
&& d
->lzma
!= NULL
)
1556 d
->lzma
->stream
.next_in
= (uint8_t *)From
;
1557 d
->lzma
->stream
.avail_in
= Size
;
1558 d
->lzma
->stream
.next_out
= d
->lzma
->buffer
;
1559 d
->lzma
->stream
.avail_out
= sizeof(d
->lzma
->buffer
)/sizeof(d
->lzma
->buffer
[0]);
1560 d
->lzma
->err
= lzma_code(&d
->lzma
->stream
, LZMA_RUN
);
1561 if (d
->lzma
->err
!= LZMA_OK
)
1563 size_t const n
= sizeof(d
->lzma
->buffer
)/sizeof(d
->lzma
->buffer
[0]) - d
->lzma
->stream
.avail_out
;
1564 size_t const m
= (n
== 0) ? 0 : fwrite(d
->lzma
->buffer
, 1, n
, d
->lzma
->file
);
1568 Res
= Size
- d
->lzma
->stream
.avail_in
;
1572 Res
= write(iFd
,From
,Size
);
1574 if (Res
< 0 && errno
== EINTR
)
1579 /* dummy so that the rest can be 'else if's */;
1581 else if (d
!= NULL
&& d
->gz
!= NULL
)
1584 char const * const errmsg
= gzerror(d
->gz
, &err
);
1586 return FileFdError("gzwrite: %s (%d: %s)", _("Write error"), err
, errmsg
);
1590 else if (d
!= NULL
&& d
->bz2
!= NULL
)
1593 char const * const errmsg
= BZ2_bzerror(d
->bz2
, &err
);
1594 if (err
!= BZ_IO_ERROR
)
1595 return FileFdError("BZ2_bzwrite: %s (%d: %s)", _("Write error"), err
, errmsg
);
1599 else if (d
!= NULL
&& d
->lzma
!= NULL
)
1600 return FileFdErrno("lzma_fwrite", _("Write error"));
1602 return FileFdErrno("write",_("Write error"));
1605 From
= (char const *)From
+ Res
;
1610 while (Res
> 0 && Size
> 0);
1615 return FileFdError(_("write, still have %llu to write but couldn't"), Size
);
1617 bool FileFd::Write(int Fd
, const void *From
, unsigned long long Size
)
1623 Res
= write(Fd
,From
,Size
);
1624 if (Res
< 0 && errno
== EINTR
)
1627 return _error
->Errno("write",_("Write error"));
1629 From
= (char const *)From
+ Res
;
1632 while (Res
> 0 && Size
> 0);
1637 return _error
->Error(_("write, still have %llu to write but couldn't"), Size
);
1640 // FileFd::Seek - Seek in the file /*{{{*/
1641 // ---------------------------------------------------------------------
1643 bool FileFd::Seek(unsigned long long To
)
1645 if (d
!= NULL
&& (d
->pipe
== true || d
->InternalStream() == true))
1647 // Our poor man seeking in pipes is costly, so try to avoid it
1648 unsigned long long seekpos
= Tell();
1651 else if (seekpos
< To
)
1652 return Skip(To
- seekpos
);
1654 if ((d
->openmode
& ReadOnly
) != ReadOnly
)
1655 return FileFdError("Reopen is only implemented for read-only files!");
1656 d
->InternalClose(FileName
);
1660 if (TemporaryFileName
.empty() == false)
1661 iFd
= open(TemporaryFileName
.c_str(), O_RDONLY
);
1662 else if (FileName
.empty() == false)
1663 iFd
= open(FileName
.c_str(), O_RDONLY
);
1666 if (d
->compressed_fd
> 0)
1667 if (lseek(d
->compressed_fd
, 0, SEEK_SET
) != 0)
1668 iFd
= d
->compressed_fd
;
1670 return FileFdError("Reopen is not implemented for pipes opened with FileFd::OpenDescriptor()!");
1673 if (OpenInternDescriptor(d
->openmode
, d
->compressor
) == false)
1674 return FileFdError("Seek on file %s because it couldn't be reopened", FileName
.c_str());
1684 if (d
!= NULL
&& d
->gz
)
1685 res
= gzseek(d
->gz
,To
,SEEK_SET
);
1688 res
= lseek(iFd
,To
,SEEK_SET
);
1689 if (res
!= (off_t
)To
)
1690 return FileFdError("Unable to seek to %llu", To
);
1697 // FileFd::Skip - Seek in the file /*{{{*/
1698 // ---------------------------------------------------------------------
1700 bool FileFd::Skip(unsigned long long Over
)
1702 if (d
!= NULL
&& (d
->pipe
== true || d
->InternalStream() == true))
1708 unsigned long long toread
= std::min((unsigned long long) sizeof(buffer
), Over
);
1709 if (Read(buffer
, toread
) == false)
1710 return FileFdError("Unable to seek ahead %llu",Over
);
1718 if (d
!= NULL
&& d
->gz
!= NULL
)
1719 res
= gzseek(d
->gz
,Over
,SEEK_CUR
);
1722 res
= lseek(iFd
,Over
,SEEK_CUR
);
1724 return FileFdError("Unable to seek ahead %llu",Over
);
1731 // FileFd::Truncate - Truncate the file /*{{{*/
1732 // ---------------------------------------------------------------------
1734 bool FileFd::Truncate(unsigned long long To
)
1736 // truncating /dev/null is always successful - as we get an error otherwise
1737 if (To
== 0 && FileName
== "/dev/null")
1739 #if defined HAVE_ZLIB || defined HAVE_BZ2 || defined HAVE_LZMA
1740 if (d
!= NULL
&& (d
->InternalStream() == true
1745 return FileFdError("Truncating compressed files is not implemented (%s)", FileName
.c_str());
1747 if (ftruncate(iFd
,To
) != 0)
1748 return FileFdError("Unable to truncate to %llu",To
);
1753 // FileFd::Tell - Current seek position /*{{{*/
1754 // ---------------------------------------------------------------------
1756 unsigned long long FileFd::Tell()
1758 // In theory, we could just return seekpos here always instead of
1759 // seeking around, but not all users of FileFd use always Seek() and co
1760 // so d->seekpos isn't always true and we can just use it as a hint if
1761 // we have nothing else, but not always as an authority…
1762 if (d
!= NULL
&& (d
->pipe
== true || d
->InternalStream() == true))
1767 if (d
!= NULL
&& d
->gz
!= NULL
)
1768 Res
= gztell(d
->gz
);
1771 Res
= lseek(iFd
,0,SEEK_CUR
);
1772 if (Res
== (off_t
)-1)
1773 FileFdErrno("lseek","Failed to determine the current file position");
1779 static bool StatFileFd(char const * const msg
, int const iFd
, std::string
const &FileName
, struct stat
&Buf
, FileFdPrivate
* const d
) /*{{{*/
1781 bool ispipe
= (d
!= NULL
&& d
->pipe
== true);
1782 if (ispipe
== false)
1784 if (fstat(iFd
,&Buf
) != 0)
1785 // higher-level code will generate more meaningful messages,
1786 // even translated this would be meaningless for users
1787 return _error
->Errno("fstat", "Unable to determine %s for fd %i", msg
, iFd
);
1788 ispipe
= S_ISFIFO(Buf
.st_mode
);
1791 // for compressor pipes st_size is undefined and at 'best' zero
1794 // we set it here, too, as we get the info here for free
1795 // in theory the Open-methods should take care of it already
1798 if (stat(FileName
.c_str(), &Buf
) != 0)
1799 return _error
->Errno("fstat", "Unable to determine %s for file %s", msg
, FileName
.c_str());
1804 // FileFd::FileSize - Return the size of the file /*{{{*/
1805 unsigned long long FileFd::FileSize()
1808 if (StatFileFd("file size", iFd
, FileName
, Buf
, d
) == false)
1816 // FileFd::ModificationTime - Return the time of last touch /*{{{*/
1817 time_t FileFd::ModificationTime()
1820 if (StatFileFd("modification time", iFd
, FileName
, Buf
, d
) == false)
1825 return Buf
.st_mtime
;
1828 // FileFd::Size - Return the size of the content in the file /*{{{*/
1829 // ---------------------------------------------------------------------
1831 unsigned long long FileFd::Size()
1833 unsigned long long size
= FileSize();
1835 // for compressor pipes st_size is undefined and at 'best' zero,
1836 // so we 'read' the content and 'seek' back - see there
1837 if (d
!= NULL
&& (d
->pipe
== true || (d
->InternalStream() == true && size
> 0)))
1839 unsigned long long const oldSeek
= Tell();
1841 unsigned long long read
= 0;
1843 if (Read(ignore
, sizeof(ignore
), &read
) == false)
1853 // only check gzsize if we are actually a gzip file, just checking for
1854 // "gz" is not sufficient as uncompressed files could be opened with
1855 // gzopen in "direct" mode as well
1856 else if (d
!= NULL
&& d
->gz
&& !gzdirect(d
->gz
) && size
> 0)
1858 off_t
const oldPos
= lseek(iFd
,0,SEEK_CUR
);
1859 /* unfortunately zlib.h doesn't provide a gzsize(), so we have to do
1860 * this ourselves; the original (uncompressed) file size is the last 32
1861 * bits of the file */
1862 // FIXME: Size for gz-files is limited by 32bit… no largefile support
1863 if (lseek(iFd
, -4, SEEK_END
) < 0)
1865 FileFdErrno("lseek","Unable to seek to end of gzipped file");
1869 if (read(iFd
, &size
, 4) != 4)
1871 FileFdErrno("read","Unable to read original size of gzipped file");
1875 #ifdef WORDS_BIGENDIAN
1876 uint32_t tmp_size
= size
;
1877 uint8_t const * const p
= (uint8_t const * const) &tmp_size
;
1878 tmp_size
= (p
[3] << 24) | (p
[2] << 16) | (p
[1] << 8) | p
[0];
1882 if (lseek(iFd
, oldPos
, SEEK_SET
) < 0)
1884 FileFdErrno("lseek","Unable to seek in gzipped file");
1895 // FileFd::Close - Close the file if the close flag is set /*{{{*/
1896 // ---------------------------------------------------------------------
1898 bool FileFd::Close()
1904 if ((Flags
& AutoClose
) == AutoClose
)
1906 if ((Flags
& Compressed
) != Compressed
&& iFd
> 0 && close(iFd
) != 0)
1907 Res
&= _error
->Errno("close",_("Problem closing the file %s"), FileName
.c_str());
1911 Res
&= d
->CloseDown(FileName
);
1917 if ((Flags
& Replace
) == Replace
) {
1918 if (rename(TemporaryFileName
.c_str(), FileName
.c_str()) != 0)
1919 Res
&= _error
->Errno("rename",_("Problem renaming the file %s to %s"), TemporaryFileName
.c_str(), FileName
.c_str());
1921 FileName
= TemporaryFileName
; // for the unlink() below.
1922 TemporaryFileName
.clear();
1927 if ((Flags
& Fail
) == Fail
&& (Flags
& DelOnFail
) == DelOnFail
&&
1928 FileName
.empty() == false)
1929 if (unlink(FileName
.c_str()) != 0)
1930 Res
&= _error
->WarningE("unlnk",_("Problem unlinking the file %s"), FileName
.c_str());
1937 // FileFd::Sync - Sync the file /*{{{*/
1938 // ---------------------------------------------------------------------
1942 if (fsync(iFd
) != 0)
1943 return FileFdErrno("sync",_("Problem syncing the file"));
1947 // FileFd::FileFdErrno - set Fail and call _error->Errno *{{{*/
1948 bool FileFd::FileFdErrno(const char *Function
, const char *Description
,...)
1952 size_t msgSize
= 400;
1953 int const errsv
= errno
;
1956 va_start(args
,Description
);
1957 if (_error
->InsertErrno(GlobalError::ERROR
, Function
, Description
, args
, errsv
, msgSize
) == false)
1964 // FileFd::FileFdError - set Fail and call _error->Error *{{{*/
1965 bool FileFd::FileFdError(const char *Description
,...) {
1968 size_t msgSize
= 400;
1971 va_start(args
,Description
);
1972 if (_error
->Insert(GlobalError::ERROR
, Description
, args
, msgSize
) == false)
1980 APT_DEPRECATED gzFile
FileFd::gzFd() {
1989 // Glob - wrapper around "glob()" /*{{{*/
1990 // ---------------------------------------------------------------------
1992 std::vector
<std::string
> Glob(std::string
const &pattern
, int flags
)
1994 std::vector
<std::string
> result
;
1999 glob_res
= glob(pattern
.c_str(), flags
, NULL
, &globbuf
);
2003 if(glob_res
!= GLOB_NOMATCH
) {
2004 _error
->Errno("glob", "Problem with glob");
2010 for(i
=0;i
<globbuf
.gl_pathc
;i
++)
2011 result
.push_back(string(globbuf
.gl_pathv
[i
]));
2018 std::string
GetTempDir()
2020 const char *tmpdir
= getenv("TMPDIR");
2027 // check that tmpdir is set and exists
2029 if (!tmpdir
|| strlen(tmpdir
) == 0 || stat(tmpdir
, &st
) != 0)
2032 return string(tmpdir
);
2035 bool Rename(std::string From
, std::string To
)
2037 if (rename(From
.c_str(),To
.c_str()) != 0)
2039 _error
->Error(_("rename failed, %s (%s -> %s)."),strerror(errno
),
2040 From
.c_str(),To
.c_str());