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>
56 #ifdef WORDS_BIGENDIAN
80 APT::Configuration::Compressor compressor
;
81 unsigned int openmode
;
82 unsigned long long seekpos
;
83 FileFdPrivate() : gz(NULL
), bz2(NULL
),
84 compressed_fd(-1), compressor_pid(-1), pipe(false),
85 openmode(0), seekpos(0) {};
86 bool CloseDown(std::string
const &FileName
)
91 int const e
= gzclose(gz
);
93 // gzdclose() on empty files always fails with "buffer error" here, ignore that
94 if (e
!= 0 && e
!= Z_BUF_ERROR
)
95 Res
&= _error
->Errno("close",_("Problem closing the gzip file %s"), FileName
.c_str());
104 if (compressor_pid
> 0)
105 ExecWait(compressor_pid
, "FileFdCompressor", true);
110 ~FileFdPrivate() { CloseDown(""); }
113 // RunScripts - Run a set of scripts from a configuration subtree /*{{{*/
114 // ---------------------------------------------------------------------
116 bool RunScripts(const char *Cnf
)
118 Configuration::Item
const *Opts
= _config
->Tree(Cnf
);
119 if (Opts
== 0 || Opts
->Child
== 0)
123 // Fork for running the system calls
124 pid_t Child
= ExecFork();
129 if (_config
->FindDir("DPkg::Chroot-Directory","/") != "/")
131 std::cerr
<< "Chrooting into "
132 << _config
->FindDir("DPkg::Chroot-Directory")
134 if (chroot(_config
->FindDir("DPkg::Chroot-Directory","/").c_str()) != 0)
138 if (chdir("/tmp/") != 0)
141 unsigned int Count
= 1;
142 for (; Opts
!= 0; Opts
= Opts
->Next
, Count
++)
144 if (Opts
->Value
.empty() == true)
147 if (system(Opts
->Value
.c_str()) != 0)
153 // Wait for the child
155 while (waitpid(Child
,&Status
,0) != Child
)
159 return _error
->Errno("waitpid","Couldn't wait for subprocess");
162 // Restore sig int/quit
163 signal(SIGQUIT
,SIG_DFL
);
164 signal(SIGINT
,SIG_DFL
);
166 // Check for an error code.
167 if (WIFEXITED(Status
) == 0 || WEXITSTATUS(Status
) != 0)
169 unsigned int Count
= WEXITSTATUS(Status
);
173 for (; Opts
!= 0 && Count
!= 1; Opts
= Opts
->Next
, Count
--);
174 _error
->Error("Problem executing scripts %s '%s'",Cnf
,Opts
->Value
.c_str());
177 return _error
->Error("Sub-process returned an error code");
184 // CopyFile - Buffered copy of a file /*{{{*/
185 // ---------------------------------------------------------------------
186 /* The caller is expected to set things so that failure causes erasure */
187 bool CopyFile(FileFd
&From
,FileFd
&To
)
189 if (From
.IsOpen() == false || To
.IsOpen() == false)
192 // Buffered copy between fds
193 SPtrArray
<unsigned char> Buf
= new unsigned char[64000];
194 unsigned long long Size
= From
.Size();
197 unsigned long long ToRead
= Size
;
201 if (From
.Read(Buf
,ToRead
) == false ||
202 To
.Write(Buf
,ToRead
) == false)
211 // GetLock - Gets a lock file /*{{{*/
212 // ---------------------------------------------------------------------
213 /* This will create an empty file of the given name and lock it. Once this
214 is done all other calls to GetLock in any other process will fail with
215 -1. The return result is the fd of the file, the call should call
216 close at some time. */
217 int GetLock(string File
,bool Errors
)
219 // GetLock() is used in aptitude on directories with public-write access
220 // Use O_NOFOLLOW here to prevent symlink traversal attacks
221 int FD
= open(File
.c_str(),O_RDWR
| O_CREAT
| O_NOFOLLOW
,0640);
224 // Read only .. cant have locking problems there.
227 _error
->Warning(_("Not using locking for read only lock file %s"),File
.c_str());
228 return dup(0); // Need something for the caller to close
232 _error
->Errno("open",_("Could not open lock file %s"),File
.c_str());
234 // Feh.. We do this to distinguish the lock vs open case..
238 SetCloseExec(FD
,true);
240 // Aquire a write lock
243 fl
.l_whence
= SEEK_SET
;
246 if (fcntl(FD
,F_SETLK
,&fl
) == -1)
250 _error
->Warning(_("Not using locking for nfs mounted lock file %s"),File
.c_str());
251 return dup(0); // Need something for the caller to close
254 _error
->Errno("open",_("Could not get lock %s"),File
.c_str());
265 // FileExists - Check if a file exists /*{{{*/
266 // ---------------------------------------------------------------------
267 /* Beware: Directories are also files! */
268 bool FileExists(string File
)
271 if (stat(File
.c_str(),&Buf
) != 0)
276 // RealFileExists - Check if a file exists and if it is really a file /*{{{*/
277 // ---------------------------------------------------------------------
279 bool RealFileExists(string File
)
282 if (stat(File
.c_str(),&Buf
) != 0)
284 return ((Buf
.st_mode
& S_IFREG
) != 0);
287 // DirectoryExists - Check if a directory exists and is really one /*{{{*/
288 // ---------------------------------------------------------------------
290 bool DirectoryExists(string
const &Path
)
293 if (stat(Path
.c_str(),&Buf
) != 0)
295 return ((Buf
.st_mode
& S_IFDIR
) != 0);
298 // CreateDirectory - poor man's mkdir -p guarded by a parent directory /*{{{*/
299 // ---------------------------------------------------------------------
300 /* This method will create all directories needed for path in good old
301 mkdir -p style but refuses to do this if Parent is not a prefix of
302 this Path. Example: /var/cache/ and /var/cache/apt/archives are given,
303 so it will create apt/archives if /var/cache exists - on the other
304 hand if the parent is /var/lib the creation will fail as this path
305 is not a parent of the path to be generated. */
306 bool CreateDirectory(string
const &Parent
, string
const &Path
)
308 if (Parent
.empty() == true || Path
.empty() == true)
311 if (DirectoryExists(Path
) == true)
314 if (DirectoryExists(Parent
) == false)
317 // we are not going to create directories "into the blue"
318 if (Path
.find(Parent
, 0) != 0)
321 vector
<string
> const dirs
= VectorizeString(Path
.substr(Parent
.size()), '/');
322 string progress
= Parent
;
323 for (vector
<string
>::const_iterator d
= dirs
.begin(); d
!= dirs
.end(); ++d
)
325 if (d
->empty() == true)
328 progress
.append("/").append(*d
);
329 if (DirectoryExists(progress
) == true)
332 if (mkdir(progress
.c_str(), 0755) != 0)
338 // CreateAPTDirectoryIfNeeded - ensure that the given directory exists /*{{{*/
339 // ---------------------------------------------------------------------
340 /* a small wrapper around CreateDirectory to check if it exists and to
341 remove the trailing "/apt/" from the parent directory if needed */
342 bool CreateAPTDirectoryIfNeeded(string
const &Parent
, string
const &Path
)
344 if (DirectoryExists(Path
) == true)
347 size_t const len
= Parent
.size();
348 if (len
> 5 && Parent
.find("/apt/", len
- 6, 5) == len
- 5)
350 if (CreateDirectory(Parent
.substr(0,len
-5), Path
) == true)
353 else if (CreateDirectory(Parent
, Path
) == true)
359 // GetListOfFilesInDir - returns a vector of files in the given dir /*{{{*/
360 // ---------------------------------------------------------------------
361 /* If an extension is given only files with this extension are included
362 in the returned vector, otherwise every "normal" file is included. */
363 std::vector
<string
> GetListOfFilesInDir(string
const &Dir
, string
const &Ext
,
364 bool const &SortList
, bool const &AllowNoExt
)
366 std::vector
<string
> ext
;
368 if (Ext
.empty() == false)
370 if (AllowNoExt
== true && ext
.empty() == false)
372 return GetListOfFilesInDir(Dir
, ext
, SortList
);
374 std::vector
<string
> GetListOfFilesInDir(string
const &Dir
, std::vector
<string
> const &Ext
,
375 bool const &SortList
)
377 // Attention debuggers: need to be set with the environment config file!
378 bool const Debug
= _config
->FindB("Debug::GetListOfFilesInDir", false);
381 std::clog
<< "Accept in " << Dir
<< " only files with the following " << Ext
.size() << " extensions:" << std::endl
;
382 if (Ext
.empty() == true)
383 std::clog
<< "\tNO extension" << std::endl
;
385 for (std::vector
<string
>::const_iterator e
= Ext
.begin();
387 std::clog
<< '\t' << (e
->empty() == true ? "NO" : *e
) << " extension" << std::endl
;
390 std::vector
<string
> List
;
392 if (DirectoryExists(Dir
.c_str()) == false)
394 _error
->Error(_("List of files can't be created as '%s' is not a directory"), Dir
.c_str());
398 Configuration::MatchAgainstConfig
SilentIgnore("Dir::Ignore-Files-Silently");
399 DIR *D
= opendir(Dir
.c_str());
402 _error
->Errno("opendir",_("Unable to read %s"),Dir
.c_str());
406 for (struct dirent
*Ent
= readdir(D
); Ent
!= 0; Ent
= readdir(D
))
408 // skip "hidden" files
409 if (Ent
->d_name
[0] == '.')
412 // Make sure it is a file and not something else
413 string
const File
= flCombine(Dir
,Ent
->d_name
);
414 #ifdef _DIRENT_HAVE_D_TYPE
415 if (Ent
->d_type
!= DT_REG
)
418 if (RealFileExists(File
.c_str()) == false)
420 // do not show ignoration warnings for directories
422 #ifdef _DIRENT_HAVE_D_TYPE
423 Ent
->d_type
== DT_DIR
||
425 DirectoryExists(File
.c_str()) == true)
427 if (SilentIgnore
.Match(Ent
->d_name
) == false)
428 _error
->Notice(_("Ignoring '%s' in directory '%s' as it is not a regular file"), Ent
->d_name
, Dir
.c_str());
433 // check for accepted extension:
434 // no extension given -> periods are bad as hell!
435 // extensions given -> "" extension allows no extension
436 if (Ext
.empty() == false)
438 string d_ext
= flExtension(Ent
->d_name
);
439 if (d_ext
== Ent
->d_name
) // no extension
441 if (std::find(Ext
.begin(), Ext
.end(), "") == Ext
.end())
444 std::clog
<< "Bad file: " << Ent
->d_name
<< " → no extension" << std::endl
;
445 if (SilentIgnore
.Match(Ent
->d_name
) == false)
446 _error
->Notice(_("Ignoring file '%s' in directory '%s' as it has no filename extension"), Ent
->d_name
, Dir
.c_str());
450 else if (std::find(Ext
.begin(), Ext
.end(), d_ext
) == Ext
.end())
453 std::clog
<< "Bad file: " << Ent
->d_name
<< " → bad extension »" << flExtension(Ent
->d_name
) << "«" << std::endl
;
454 if (SilentIgnore
.Match(Ent
->d_name
) == false)
455 _error
->Notice(_("Ignoring file '%s' in directory '%s' as it has an invalid filename extension"), Ent
->d_name
, Dir
.c_str());
460 // Skip bad filenames ala run-parts
461 const char *C
= Ent
->d_name
;
463 if (isalpha(*C
) == 0 && isdigit(*C
) == 0
464 && *C
!= '_' && *C
!= '-') {
465 // no required extension -> dot is a bad character
466 if (*C
== '.' && Ext
.empty() == false)
471 // we don't reach the end of the name -> bad character included
475 std::clog
<< "Bad file: " << Ent
->d_name
<< " → bad character »"
476 << *C
<< "« in filename (period allowed: " << (Ext
.empty() ? "no" : "yes") << ")" << std::endl
;
480 // skip filenames which end with a period. These are never valid
484 std::clog
<< "Bad file: " << Ent
->d_name
<< " → Period as last character" << std::endl
;
489 std::clog
<< "Accept file: " << Ent
->d_name
<< " in " << Dir
<< std::endl
;
490 List
.push_back(File
);
494 if (SortList
== true)
495 std::sort(List
.begin(),List
.end());
498 std::vector
<string
> GetListOfFilesInDir(string
const &Dir
, bool SortList
)
500 bool const Debug
= _config
->FindB("Debug::GetListOfFilesInDir", false);
502 std::clog
<< "Accept in " << Dir
<< " all regular files" << std::endl
;
504 std::vector
<string
> List
;
506 if (DirectoryExists(Dir
.c_str()) == false)
508 _error
->Error(_("List of files can't be created as '%s' is not a directory"), Dir
.c_str());
512 DIR *D
= opendir(Dir
.c_str());
515 _error
->Errno("opendir",_("Unable to read %s"),Dir
.c_str());
519 for (struct dirent
*Ent
= readdir(D
); Ent
!= 0; Ent
= readdir(D
))
521 // skip "hidden" files
522 if (Ent
->d_name
[0] == '.')
525 // Make sure it is a file and not something else
526 string
const File
= flCombine(Dir
,Ent
->d_name
);
527 #ifdef _DIRENT_HAVE_D_TYPE
528 if (Ent
->d_type
!= DT_REG
)
531 if (RealFileExists(File
.c_str()) == false)
534 std::clog
<< "Bad file: " << Ent
->d_name
<< " → it is not a real file" << std::endl
;
539 // Skip bad filenames ala run-parts
540 const char *C
= Ent
->d_name
;
542 if (isalpha(*C
) == 0 && isdigit(*C
) == 0
543 && *C
!= '_' && *C
!= '-' && *C
!= '.')
546 // we don't reach the end of the name -> bad character included
550 std::clog
<< "Bad file: " << Ent
->d_name
<< " → bad character »" << *C
<< "« in filename" << std::endl
;
554 // skip filenames which end with a period. These are never valid
558 std::clog
<< "Bad file: " << Ent
->d_name
<< " → Period as last character" << std::endl
;
563 std::clog
<< "Accept file: " << Ent
->d_name
<< " in " << Dir
<< std::endl
;
564 List
.push_back(File
);
568 if (SortList
== true)
569 std::sort(List
.begin(),List
.end());
573 // SafeGetCWD - This is a safer getcwd that returns a dynamic string /*{{{*/
574 // ---------------------------------------------------------------------
575 /* We return / on failure. */
578 // Stash the current dir.
581 if (getcwd(S
,sizeof(S
)-2) == 0)
583 unsigned int Len
= strlen(S
);
589 // GetModificationTime - Get the mtime of the given file or -1 on error /*{{{*/
590 // ---------------------------------------------------------------------
591 /* We return / on failure. */
592 time_t GetModificationTime(string
const &Path
)
595 if (stat(Path
.c_str(), &St
) < 0)
600 // flNotDir - Strip the directory from the filename /*{{{*/
601 // ---------------------------------------------------------------------
603 string
flNotDir(string File
)
605 string::size_type Res
= File
.rfind('/');
606 if (Res
== string::npos
)
609 return string(File
,Res
,Res
- File
.length());
612 // flNotFile - Strip the file from the directory name /*{{{*/
613 // ---------------------------------------------------------------------
614 /* Result ends in a / */
615 string
flNotFile(string File
)
617 string::size_type Res
= File
.rfind('/');
618 if (Res
== string::npos
)
621 return string(File
,0,Res
);
624 // flExtension - Return the extension for the file /*{{{*/
625 // ---------------------------------------------------------------------
627 string
flExtension(string File
)
629 string::size_type Res
= File
.rfind('.');
630 if (Res
== string::npos
)
633 return string(File
,Res
,Res
- File
.length());
636 // flNoLink - If file is a symlink then deref it /*{{{*/
637 // ---------------------------------------------------------------------
638 /* If the name is not a link then the returned path is the input. */
639 string
flNoLink(string File
)
642 if (lstat(File
.c_str(),&St
) != 0 || S_ISLNK(St
.st_mode
) == 0)
644 if (stat(File
.c_str(),&St
) != 0)
647 /* Loop resolving the link. There is no need to limit the number of
648 loops because the stat call above ensures that the symlink is not
656 if ((Res
= readlink(NFile
.c_str(),Buffer
,sizeof(Buffer
))) <= 0 ||
657 (unsigned)Res
>= sizeof(Buffer
))
660 // Append or replace the previous path
662 if (Buffer
[0] == '/')
665 NFile
= flNotFile(NFile
) + Buffer
;
667 // See if we are done
668 if (lstat(NFile
.c_str(),&St
) != 0)
670 if (S_ISLNK(St
.st_mode
) == 0)
675 // flCombine - Combine a file and a directory /*{{{*/
676 // ---------------------------------------------------------------------
677 /* If the file is an absolute path then it is just returned, otherwise
678 the directory is pre-pended to it. */
679 string
flCombine(string Dir
,string File
)
681 if (File
.empty() == true)
684 if (File
[0] == '/' || Dir
.empty() == true)
686 if (File
.length() >= 2 && File
[0] == '.' && File
[1] == '/')
688 if (Dir
[Dir
.length()-1] == '/')
690 return Dir
+ '/' + File
;
693 // SetCloseExec - Set the close on exec flag /*{{{*/
694 // ---------------------------------------------------------------------
696 void SetCloseExec(int Fd
,bool Close
)
698 if (fcntl(Fd
,F_SETFD
,(Close
== false)?0:FD_CLOEXEC
) != 0)
700 cerr
<< "FATAL -> Could not set close on exec " << strerror(errno
) << endl
;
705 // SetNonBlock - Set the nonblocking flag /*{{{*/
706 // ---------------------------------------------------------------------
708 void SetNonBlock(int Fd
,bool Block
)
710 int Flags
= fcntl(Fd
,F_GETFL
) & (~O_NONBLOCK
);
711 if (fcntl(Fd
,F_SETFL
,Flags
| ((Block
== false)?0:O_NONBLOCK
)) != 0)
713 cerr
<< "FATAL -> Could not set non-blocking flag " << strerror(errno
) << endl
;
718 // WaitFd - Wait for a FD to become readable /*{{{*/
719 // ---------------------------------------------------------------------
720 /* This waits for a FD to become readable using select. It is useful for
721 applications making use of non-blocking sockets. The timeout is
723 bool WaitFd(int Fd
,bool write
,unsigned long timeout
)
736 Res
= select(Fd
+1,0,&Set
,0,(timeout
!= 0?&tv
:0));
738 while (Res
< 0 && errno
== EINTR
);
748 Res
= select(Fd
+1,&Set
,0,0,(timeout
!= 0?&tv
:0));
750 while (Res
< 0 && errno
== EINTR
);
759 // ExecFork - Magical fork that sanitizes the context before execing /*{{{*/
760 // ---------------------------------------------------------------------
761 /* This is used if you want to cleanse the environment for the forked
762 child, it fixes up the important signals and nukes all of the fds,
763 otherwise acts like normal fork. */
766 // Fork off the process
767 pid_t Process
= fork();
770 cerr
<< "FATAL -> Failed to fork." << endl
;
774 // Spawn the subprocess
778 signal(SIGPIPE
,SIG_DFL
);
779 signal(SIGQUIT
,SIG_DFL
);
780 signal(SIGINT
,SIG_DFL
);
781 signal(SIGWINCH
,SIG_DFL
);
782 signal(SIGCONT
,SIG_DFL
);
783 signal(SIGTSTP
,SIG_DFL
);
786 Configuration::Item
const *Opts
= _config
->Tree("APT::Keep-Fds");
787 if (Opts
!= 0 && Opts
->Child
!= 0)
790 for (; Opts
!= 0; Opts
= Opts
->Next
)
792 if (Opts
->Value
.empty() == true)
794 int fd
= atoi(Opts
->Value
.c_str());
799 // Close all of our FDs - just in case
800 for (int K
= 3; K
!= 40; K
++)
802 if(KeepFDs
.find(K
) == KeepFDs
.end())
803 fcntl(K
,F_SETFD
,FD_CLOEXEC
);
810 // ExecWait - Fancy waitpid /*{{{*/
811 // ---------------------------------------------------------------------
812 /* Waits for the given sub process. If Reap is set then no errors are
813 generated. Otherwise a failed subprocess will generate a proper descriptive
815 bool ExecWait(pid_t Pid
,const char *Name
,bool Reap
)
820 // Wait and collect the error code
822 while (waitpid(Pid
,&Status
,0) != Pid
)
830 return _error
->Error(_("Waited for %s but it wasn't there"),Name
);
834 // Check for an error code.
835 if (WIFEXITED(Status
) == 0 || WEXITSTATUS(Status
) != 0)
839 if (WIFSIGNALED(Status
) != 0)
841 if( WTERMSIG(Status
) == SIGSEGV
)
842 return _error
->Error(_("Sub-process %s received a segmentation fault."),Name
);
844 return _error
->Error(_("Sub-process %s received signal %u."),Name
, WTERMSIG(Status
));
847 if (WIFEXITED(Status
) != 0)
848 return _error
->Error(_("Sub-process %s returned an error code (%u)"),Name
,WEXITSTATUS(Status
));
850 return _error
->Error(_("Sub-process %s exited unexpectedly"),Name
);
857 // StartsWithGPGClearTextSignature - Check if a file is Pgp/GPG clearsigned /*{{{*/
858 // ---------------------------------------------------------------------
860 bool StartsWithGPGClearTextSignature(string
const &FileName
)
862 static const char* SIGMSG
= "-----BEGIN PGP SIGNED MESSAGE-----\n";
863 char buffer
[strlen(SIGMSG
)+1];
864 FILE* gpg
= fopen(FileName
.c_str(), "r");
868 char const * const test
= fgets(buffer
, sizeof(buffer
), gpg
);
870 if (test
== NULL
|| strcmp(buffer
, SIGMSG
) != 0)
877 // FileFd::Open - Open a file /*{{{*/
878 // ---------------------------------------------------------------------
879 /* The most commonly used open mode combinations are given with Mode */
880 bool FileFd::Open(string FileName
,unsigned int const Mode
,CompressMode Compress
, unsigned long const Perms
)
882 if (Mode
== ReadOnlyGzip
)
883 return Open(FileName
, ReadOnly
, Gzip
, Perms
);
885 if (Compress
== Auto
&& (Mode
& WriteOnly
) == WriteOnly
)
886 return _error
->Error("Autodetection on %s only works in ReadOnly openmode!", FileName
.c_str());
888 std::vector
<APT::Configuration::Compressor
> const compressors
= APT::Configuration::getCompressors();
889 std::vector
<APT::Configuration::Compressor
>::const_iterator compressor
= compressors
.begin();
890 if (Compress
== Auto
)
892 for (; compressor
!= compressors
.end(); ++compressor
)
894 std::string file
= std::string(FileName
).append(compressor
->Extension
);
895 if (FileExists(file
) == false)
901 else if (Compress
== Extension
)
903 std::string::size_type
const found
= FileName
.find_last_of('.');
905 if (found
!= std::string::npos
)
907 ext
= FileName
.substr(found
);
908 if (ext
== ".new" || ext
== ".bak")
910 std::string::size_type
const found2
= FileName
.find_last_of('.', found
- 1);
911 if (found2
!= std::string::npos
)
912 ext
= FileName
.substr(found2
, found
- found2
);
917 for (; compressor
!= compressors
.end(); ++compressor
)
918 if (ext
== compressor
->Extension
)
920 // no matching extension - assume uncompressed (imagine files like 'example.org_Packages')
921 if (compressor
== compressors
.end())
922 for (compressor
= compressors
.begin(); compressor
!= compressors
.end(); ++compressor
)
923 if (compressor
->Name
== ".")
931 case None
: name
= "."; break;
932 case Gzip
: name
= "gzip"; break;
933 case Bzip2
: name
= "bzip2"; break;
934 case Lzma
: name
= "lzma"; break;
935 case Xz
: name
= "xz"; break;
939 return _error
->Error("Opening File %s in None, Auto or Extension should be already handled?!?", FileName
.c_str());
941 for (; compressor
!= compressors
.end(); ++compressor
)
942 if (compressor
->Name
== name
)
944 if (compressor
== compressors
.end())
945 return _error
->Error("Can't find a configured compressor %s for file %s", name
.c_str(), FileName
.c_str());
948 if (compressor
== compressors
.end())
949 return _error
->Error("Can't find a match for specified compressor mode for file %s", FileName
.c_str());
950 return Open(FileName
, Mode
, *compressor
, Perms
);
952 bool FileFd::Open(string FileName
,unsigned int const Mode
,APT::Configuration::Compressor
const &compressor
, unsigned long const Perms
)
957 if ((Mode
& WriteOnly
) != WriteOnly
&& (Mode
& (Atomic
| Create
| Empty
| Exclusive
)) != 0)
958 return _error
->Error("ReadOnly mode for %s doesn't accept additional flags!", FileName
.c_str());
959 if ((Mode
& ReadWrite
) == 0)
960 return _error
->Error("No openmode provided in FileFd::Open for %s", FileName
.c_str());
962 if ((Mode
& Atomic
) == Atomic
)
965 char *name
= strdup((FileName
+ ".XXXXXX").c_str());
966 TemporaryFileName
= string(mktemp(name
));
969 else if ((Mode
& (Exclusive
| Create
)) == (Exclusive
| Create
))
971 // for atomic, this will be done by rename in Close()
972 unlink(FileName
.c_str());
974 if ((Mode
& Empty
) == Empty
)
977 if (lstat(FileName
.c_str(),&Buf
) == 0 && S_ISLNK(Buf
.st_mode
))
978 unlink(FileName
.c_str());
982 #define if_FLAGGED_SET(FLAG, MODE) if ((Mode & FLAG) == FLAG) fileflags |= MODE
983 if_FLAGGED_SET(ReadWrite
, O_RDWR
);
984 else if_FLAGGED_SET(ReadOnly
, O_RDONLY
);
985 else if_FLAGGED_SET(WriteOnly
, O_WRONLY
);
987 if_FLAGGED_SET(Create
, O_CREAT
);
988 if_FLAGGED_SET(Empty
, O_TRUNC
);
989 if_FLAGGED_SET(Exclusive
, O_EXCL
);
990 else if_FLAGGED_SET(Atomic
, O_EXCL
);
991 #undef if_FLAGGED_SET
993 if (TemporaryFileName
.empty() == false)
994 iFd
= open(TemporaryFileName
.c_str(), fileflags
, Perms
);
996 iFd
= open(FileName
.c_str(), fileflags
, Perms
);
998 this->FileName
= FileName
;
999 if (iFd
== -1 || OpenInternDescriptor(Mode
, compressor
) == false)
1006 return _error
->Errno("open",_("Could not open file %s"), FileName
.c_str());
1009 SetCloseExec(iFd
,true);
1013 // FileFd::OpenDescriptor - Open a filedescriptor /*{{{*/
1014 // ---------------------------------------------------------------------
1016 bool FileFd::OpenDescriptor(int Fd
, unsigned int const Mode
, CompressMode Compress
, bool AutoClose
)
1018 std::vector
<APT::Configuration::Compressor
> const compressors
= APT::Configuration::getCompressors();
1019 std::vector
<APT::Configuration::Compressor
>::const_iterator compressor
= compressors
.begin();
1022 // compat with the old API
1023 if (Mode
== ReadOnlyGzip
&& Compress
== None
)
1028 case None
: name
= "."; break;
1029 case Gzip
: name
= "gzip"; break;
1030 case Bzip2
: name
= "bzip2"; break;
1031 case Lzma
: name
= "lzma"; break;
1032 case Xz
: name
= "xz"; break;
1035 return _error
->Error("Opening Fd %d in Auto or Extension compression mode is not supported", Fd
);
1037 for (; compressor
!= compressors
.end(); ++compressor
)
1038 if (compressor
->Name
== name
)
1040 if (compressor
== compressors
.end())
1041 return _error
->Error("Can't find a configured compressor %s for file %s", name
.c_str(), FileName
.c_str());
1043 return OpenDescriptor(Fd
, Mode
, *compressor
, AutoClose
);
1045 bool FileFd::OpenDescriptor(int Fd
, unsigned int const Mode
, APT::Configuration::Compressor
const &compressor
, bool AutoClose
)
1048 Flags
= (AutoClose
) ? FileFd::AutoClose
: 0;
1049 if (AutoClose
== false && (
1051 compressor
.Name
== "gzip" ||
1054 compressor
.Name
== "bzip2" ||
1058 // Need to duplicate fd here or gzclose for cleanup will close the fd as well
1063 this->FileName
= "";
1064 if (OpenInternDescriptor(Mode
, compressor
) == false)
1068 return _error
->Errno("gzdopen",_("Could not open file descriptor %d"), Fd
);
1072 bool FileFd::OpenInternDescriptor(unsigned int const Mode
, APT::Configuration::Compressor
const &compressor
)
1074 if (compressor
.Name
== "." || compressor
.Binary
.empty() == true)
1079 d
= new FileFdPrivate();
1081 d
->compressor
= compressor
;
1085 if (compressor
.Name
== "gzip")
1092 if ((Mode
& ReadWrite
) == ReadWrite
)
1093 d
->gz
= gzdopen(iFd
, "r+");
1094 else if ((Mode
& WriteOnly
) == WriteOnly
)
1095 d
->gz
= gzdopen(iFd
, "w");
1097 d
->gz
= gzdopen(iFd
, "r");
1100 Flags
|= Compressed
;
1105 if (compressor
.Name
== "bzip2")
1109 BZ2_bzclose(d
->bz2
);
1112 if ((Mode
& ReadWrite
) == ReadWrite
)
1113 d
->bz2
= BZ2_bzdopen(iFd
, "r+");
1114 else if ((Mode
& WriteOnly
) == WriteOnly
)
1115 d
->bz2
= BZ2_bzdopen(iFd
, "w");
1117 d
->bz2
= BZ2_bzdopen(iFd
, "r");
1120 Flags
|= Compressed
;
1125 // collect zombies here in case we reopen
1126 if (d
->compressor_pid
> 0)
1127 ExecWait(d
->compressor_pid
, "FileFdCompressor", true);
1129 if ((Mode
& ReadWrite
) == ReadWrite
)
1132 return _error
->Error("ReadWrite mode is not supported for file %s", FileName
.c_str());
1135 bool const Comp
= (Mode
& WriteOnly
) == WriteOnly
;
1138 // Handle 'decompression' of empty files
1141 if (Buf
.st_size
== 0 && S_ISFIFO(Buf
.st_mode
) == false)
1144 // We don't need the file open - instead let the compressor open it
1145 // as he properly knows better how to efficiently read from 'his' file
1146 if (FileName
.empty() == false)
1153 // Create a data pipe
1154 int Pipe
[2] = {-1,-1};
1155 if (pipe(Pipe
) != 0)
1158 return _error
->Errno("pipe",_("Failed to create subprocess IPC"));
1160 for (int J
= 0; J
!= 2; J
++)
1161 SetCloseExec(Pipe
[J
],true);
1163 d
->compressed_fd
= iFd
;
1172 d
->compressor_pid
= ExecFork();
1173 if (d
->compressor_pid
== 0)
1177 dup2(d
->compressed_fd
,STDOUT_FILENO
);
1178 dup2(Pipe
[0],STDIN_FILENO
);
1182 if (FileName
.empty() == true)
1183 dup2(d
->compressed_fd
,STDIN_FILENO
);
1184 dup2(Pipe
[1],STDOUT_FILENO
);
1186 int const nullfd
= open("/dev/null", O_WRONLY
);
1189 dup2(nullfd
,STDERR_FILENO
);
1193 SetCloseExec(STDOUT_FILENO
,false);
1194 SetCloseExec(STDIN_FILENO
,false);
1196 std::vector
<char const*> Args
;
1197 Args
.push_back(compressor
.Binary
.c_str());
1198 std::vector
<std::string
> const * const addArgs
=
1199 (Comp
== true) ? &(compressor
.CompressArgs
) : &(compressor
.UncompressArgs
);
1200 for (std::vector
<std::string
>::const_iterator a
= addArgs
->begin();
1201 a
!= addArgs
->end(); ++a
)
1202 Args
.push_back(a
->c_str());
1203 if (Comp
== false && FileName
.empty() == false)
1205 Args
.push_back("--stdout");
1206 if (TemporaryFileName
.empty() == false)
1207 Args
.push_back(TemporaryFileName
.c_str());
1209 Args
.push_back(FileName
.c_str());
1211 Args
.push_back(NULL
);
1213 execvp(Args
[0],(char **)&Args
[0]);
1214 cerr
<< _("Failed to exec compressor ") << Args
[0] << endl
;
1225 // FileFd::~File - Closes the file /*{{{*/
1226 // ---------------------------------------------------------------------
1227 /* If the proper modes are selected then we close the Fd and possibly
1228 unlink the file on error. */
1234 d
->CloseDown(FileName
);
1240 // FileFd::Read - Read a bit of the file /*{{{*/
1241 // ---------------------------------------------------------------------
1242 /* We are carefull to handle interruption by a signal while reading
1244 bool FileFd::Read(void *To
,unsigned long long Size
,unsigned long long *Actual
)
1250 *((char *)To
) = '\0';
1254 if (d
!= NULL
&& d
->gz
!= NULL
)
1255 Res
= gzread(d
->gz
,To
,Size
);
1259 if (d
!= NULL
&& d
->bz2
!= NULL
)
1260 Res
= BZ2_bzread(d
->bz2
,To
,Size
);
1263 Res
= read(iFd
,To
,Size
);
1271 if (d
!= NULL
&& d
->gz
!= NULL
)
1274 char const * const errmsg
= gzerror(d
->gz
, &err
);
1276 return _error
->Error("gzread: %s (%d: %s)", _("Read error"), err
, errmsg
);
1280 if (d
!= NULL
&& d
->bz2
!= NULL
)
1283 char const * const errmsg
= BZ2_bzerror(d
->bz2
, &err
);
1284 if (err
!= BZ_IO_ERROR
)
1285 return _error
->Error("BZ2_bzread: %s (%d: %s)", _("Read error"), err
, errmsg
);
1288 return _error
->Errno("read",_("Read error"));
1291 To
= (char *)To
+ Res
;
1298 while (Res
> 0 && Size
> 0);
1311 return _error
->Error(_("read, still have %llu to read but none left"), Size
);
1314 // FileFd::ReadLine - Read a complete line from the file /*{{{*/
1315 // ---------------------------------------------------------------------
1316 /* Beware: This method can be quiet slow for big buffers on UNcompressed
1317 files because of the naive implementation! */
1318 char* FileFd::ReadLine(char *To
, unsigned long long const Size
)
1322 if (d
!= NULL
&& d
->gz
!= NULL
)
1323 return gzgets(d
->gz
, To
, Size
);
1326 unsigned long long read
= 0;
1327 while ((Size
- 1) != read
)
1329 unsigned long long done
= 0;
1330 if (Read(To
+ read
, 1, &done
) == false)
1334 if (To
[read
++] == '\n')
1343 // FileFd::Write - Write to the file /*{{{*/
1344 // ---------------------------------------------------------------------
1346 bool FileFd::Write(const void *From
,unsigned long long Size
)
1353 if (d
!= NULL
&& d
->gz
!= NULL
)
1354 Res
= gzwrite(d
->gz
,From
,Size
);
1358 if (d
!= NULL
&& d
->bz2
!= NULL
)
1359 Res
= BZ2_bzwrite(d
->bz2
,(void*)From
,Size
);
1362 Res
= write(iFd
,From
,Size
);
1363 if (Res
< 0 && errno
== EINTR
)
1369 if (d
!= NULL
&& d
->gz
!= NULL
)
1372 char const * const errmsg
= gzerror(d
->gz
, &err
);
1374 return _error
->Error("gzwrite: %s (%d: %s)", _("Write error"), err
, errmsg
);
1378 if (d
!= NULL
&& d
->bz2
!= NULL
)
1381 char const * const errmsg
= BZ2_bzerror(d
->bz2
, &err
);
1382 if (err
!= BZ_IO_ERROR
)
1383 return _error
->Error("BZ2_bzwrite: %s (%d: %s)", _("Write error"), err
, errmsg
);
1386 return _error
->Errno("write",_("Write error"));
1389 From
= (char *)From
+ Res
;
1394 while (Res
> 0 && Size
> 0);
1400 return _error
->Error(_("write, still have %llu to write but couldn't"), Size
);
1402 bool FileFd::Write(int Fd
, const void *From
, unsigned long long Size
)
1408 Res
= write(Fd
,From
,Size
);
1409 if (Res
< 0 && errno
== EINTR
)
1412 return _error
->Errno("write",_("Write error"));
1414 From
= (char *)From
+ Res
;
1417 while (Res
> 0 && Size
> 0);
1422 return _error
->Error(_("write, still have %llu to write but couldn't"), Size
);
1425 // FileFd::Seek - Seek in the file /*{{{*/
1426 // ---------------------------------------------------------------------
1428 bool FileFd::Seek(unsigned long long To
)
1430 if (d
!= NULL
&& (d
->pipe
== true
1436 // Our poor man seeking in pipes is costly, so try to avoid it
1437 unsigned long long seekpos
= Tell();
1440 else if (seekpos
< To
)
1441 return Skip(To
- seekpos
);
1443 if ((d
->openmode
& ReadOnly
) != ReadOnly
)
1446 return _error
->Error("Reopen is only implemented for read-only files!");
1450 BZ2_bzclose(d
->bz2
);
1455 if (TemporaryFileName
.empty() == false)
1456 iFd
= open(TemporaryFileName
.c_str(), O_RDONLY
);
1457 else if (FileName
.empty() == false)
1458 iFd
= open(FileName
.c_str(), O_RDONLY
);
1461 if (d
->compressed_fd
> 0)
1462 if (lseek(d
->compressed_fd
, 0, SEEK_SET
) != 0)
1463 iFd
= d
->compressed_fd
;
1467 return _error
->Error("Reopen is not implemented for pipes opened with FileFd::OpenDescriptor()!");
1471 if (OpenInternDescriptor(d
->openmode
, d
->compressor
) == false)
1474 return _error
->Error("Seek on file %s because it couldn't be reopened", FileName
.c_str());
1485 if (d
!= NULL
&& d
->gz
)
1486 res
= gzseek(d
->gz
,To
,SEEK_SET
);
1489 res
= lseek(iFd
,To
,SEEK_SET
);
1490 if (res
!= (signed)To
)
1493 return _error
->Error("Unable to seek to %llu", To
);
1501 // FileFd::Skip - Seek in the file /*{{{*/
1502 // ---------------------------------------------------------------------
1504 bool FileFd::Skip(unsigned long long Over
)
1506 if (d
!= NULL
&& (d
->pipe
== true
1516 unsigned long long toread
= std::min((unsigned long long) sizeof(buffer
), Over
);
1517 if (Read(buffer
, toread
) == false)
1520 return _error
->Error("Unable to seek ahead %llu",Over
);
1529 if (d
!= NULL
&& d
->gz
!= NULL
)
1530 res
= gzseek(d
->gz
,Over
,SEEK_CUR
);
1533 res
= lseek(iFd
,Over
,SEEK_CUR
);
1537 return _error
->Error("Unable to seek ahead %llu",Over
);
1545 // FileFd::Truncate - Truncate the file /*{{{*/
1546 // ---------------------------------------------------------------------
1548 bool FileFd::Truncate(unsigned long long To
)
1550 #if defined HAVE_ZLIB || defined HAVE_BZ2
1551 if (d
!= NULL
&& (d
->gz
!= NULL
|| d
->bz2
!= NULL
))
1554 return _error
->Error("Truncating compressed files is not implemented (%s)", FileName
.c_str());
1557 if (ftruncate(iFd
,To
) != 0)
1560 return _error
->Error("Unable to truncate to %llu",To
);
1566 // FileFd::Tell - Current seek position /*{{{*/
1567 // ---------------------------------------------------------------------
1569 unsigned long long FileFd::Tell()
1571 // In theory, we could just return seekpos here always instead of
1572 // seeking around, but not all users of FileFd use always Seek() and co
1573 // so d->seekpos isn't always true and we can just use it as a hint if
1574 // we have nothing else, but not always as an authority…
1575 if (d
!= NULL
&& (d
->pipe
== true
1584 if (d
!= NULL
&& d
->gz
!= NULL
)
1585 Res
= gztell(d
->gz
);
1588 Res
= lseek(iFd
,0,SEEK_CUR
);
1589 if (Res
== (off_t
)-1)
1592 _error
->Errno("lseek","Failed to determine the current file position");
1599 // FileFd::FileSize - Return the size of the file /*{{{*/
1600 // ---------------------------------------------------------------------
1602 unsigned long long FileFd::FileSize()
1605 if ((d
== NULL
|| d
->pipe
== false) && fstat(iFd
,&Buf
) != 0)
1608 return _error
->Errno("fstat","Unable to determine the file size");
1611 // for compressor pipes st_size is undefined and at 'best' zero
1612 if ((d
!= NULL
&& d
->pipe
== true) || S_ISFIFO(Buf
.st_mode
))
1614 // we set it here, too, as we get the info here for free
1615 // in theory the Open-methods should take care of it already
1618 if (stat(FileName
.c_str(), &Buf
) != 0)
1621 return _error
->Errno("stat","Unable to determine the file size");
1628 // FileFd::Size - Return the size of the content in the file /*{{{*/
1629 // ---------------------------------------------------------------------
1631 unsigned long long FileFd::Size()
1633 unsigned long long size
= FileSize();
1635 // for compressor pipes st_size is undefined and at 'best' zero,
1636 // so we 'read' the content and 'seek' back - see there
1637 if (d
!= NULL
&& (d
->pipe
== true
1639 || (d
->bz2
&& size
> 0)
1643 unsigned long long const oldSeek
= Tell();
1645 unsigned long long read
= 0;
1647 Read(ignore
, sizeof(ignore
), &read
);
1653 // only check gzsize if we are actually a gzip file, just checking for
1654 // "gz" is not sufficient as uncompressed files could be opened with
1655 // gzopen in "direct" mode as well
1656 else if (d
!= NULL
&& d
->gz
&& !gzdirect(d
->gz
) && size
> 0)
1658 off_t
const oldPos
= lseek(iFd
,0,SEEK_CUR
);
1659 /* unfortunately zlib.h doesn't provide a gzsize(), so we have to do
1660 * this ourselves; the original (uncompressed) file size is the last 32
1661 * bits of the file */
1662 // FIXME: Size for gz-files is limited by 32bit… no largefile support
1663 if (lseek(iFd
, -4, SEEK_END
) < 0)
1666 return _error
->Errno("lseek","Unable to seek to end of gzipped file");
1669 if (read(iFd
, &size
, 4) != 4)
1672 return _error
->Errno("read","Unable to read original size of gzipped file");
1675 #ifdef WORDS_BIGENDIAN
1676 uint32_t tmp_size
= size
;
1677 uint8_t const * const p
= (uint8_t const * const) &tmp_size
;
1678 tmp_size
= (p
[3] << 24) | (p
[2] << 16) | (p
[1] << 8) | p
[0];
1682 if (lseek(iFd
, oldPos
, SEEK_SET
) < 0)
1685 return _error
->Errno("lseek","Unable to seek in gzipped file");
1695 // FileFd::ModificationTime - Return the time of last touch /*{{{*/
1696 // ---------------------------------------------------------------------
1698 time_t FileFd::ModificationTime()
1701 if ((d
== NULL
|| d
->pipe
== false) && fstat(iFd
,&Buf
) != 0)
1704 _error
->Errno("fstat","Unable to determine the modification time of file %s", FileName
.c_str());
1708 // for compressor pipes st_size is undefined and at 'best' zero
1709 if ((d
!= NULL
&& d
->pipe
== true) || S_ISFIFO(Buf
.st_mode
))
1711 // we set it here, too, as we get the info here for free
1712 // in theory the Open-methods should take care of it already
1715 if (stat(FileName
.c_str(), &Buf
) != 0)
1718 _error
->Errno("fstat","Unable to determine the modification time of file %s", FileName
.c_str());
1723 return Buf
.st_mtime
;
1726 // FileFd::Close - Close the file if the close flag is set /*{{{*/
1727 // ---------------------------------------------------------------------
1729 bool FileFd::Close()
1735 if ((Flags
& AutoClose
) == AutoClose
)
1737 if ((Flags
& Compressed
) != Compressed
&& iFd
> 0 && close(iFd
) != 0)
1738 Res
&= _error
->Errno("close",_("Problem closing the file %s"), FileName
.c_str());
1742 Res
&= d
->CloseDown(FileName
);
1748 if ((Flags
& Replace
) == Replace
) {
1749 if (rename(TemporaryFileName
.c_str(), FileName
.c_str()) != 0)
1750 Res
&= _error
->Errno("rename",_("Problem renaming the file %s to %s"), TemporaryFileName
.c_str(), FileName
.c_str());
1752 FileName
= TemporaryFileName
; // for the unlink() below.
1753 TemporaryFileName
.clear();
1758 if ((Flags
& Fail
) == Fail
&& (Flags
& DelOnFail
) == DelOnFail
&&
1759 FileName
.empty() == false)
1760 if (unlink(FileName
.c_str()) != 0)
1761 Res
&= _error
->WarningE("unlnk",_("Problem unlinking the file %s"), FileName
.c_str());
1768 // FileFd::Sync - Sync the file /*{{{*/
1769 // ---------------------------------------------------------------------
1773 if (fsync(iFd
) != 0)
1776 return _error
->Errno("sync",_("Problem syncing the file"));
1782 gzFile
FileFd::gzFd() { return (gzFile
) d
->gz
; }
1785 // Glob - wrapper around "glob()" /*{{{*/
1786 // ---------------------------------------------------------------------
1788 std::vector
<std::string
> Glob(std::string
const &pattern
, int flags
)
1790 std::vector
<std::string
> result
;
1794 glob_res
= glob(pattern
.c_str(), flags
, NULL
, &globbuf
);
1798 if(glob_res
!= GLOB_NOMATCH
) {
1799 _error
->Errno("glob", "Problem with glob");
1805 for(i
=0;i
<globbuf
.gl_pathc
;i
++)
1806 result
.push_back(string(globbuf
.gl_pathv
[i
]));