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>
49 #ifdef WORDS_BIGENDIAN
61 FileFdPrivate() : gz(NULL
) {};
64 // RunScripts - Run a set of scripts from a configuration subtree /*{{{*/
65 // ---------------------------------------------------------------------
67 bool RunScripts(const char *Cnf
)
69 Configuration::Item
const *Opts
= _config
->Tree(Cnf
);
70 if (Opts
== 0 || Opts
->Child
== 0)
74 // Fork for running the system calls
75 pid_t Child
= ExecFork();
80 if (_config
->FindDir("DPkg::Chroot-Directory","/") != "/")
82 std::cerr
<< "Chrooting into "
83 << _config
->FindDir("DPkg::Chroot-Directory")
85 if (chroot(_config
->FindDir("DPkg::Chroot-Directory","/").c_str()) != 0)
89 if (chdir("/tmp/") != 0)
92 unsigned int Count
= 1;
93 for (; Opts
!= 0; Opts
= Opts
->Next
, Count
++)
95 if (Opts
->Value
.empty() == true)
98 if (system(Opts
->Value
.c_str()) != 0)
104 // Wait for the child
106 while (waitpid(Child
,&Status
,0) != Child
)
110 return _error
->Errno("waitpid","Couldn't wait for subprocess");
113 // Restore sig int/quit
114 signal(SIGQUIT
,SIG_DFL
);
115 signal(SIGINT
,SIG_DFL
);
117 // Check for an error code.
118 if (WIFEXITED(Status
) == 0 || WEXITSTATUS(Status
) != 0)
120 unsigned int Count
= WEXITSTATUS(Status
);
124 for (; Opts
!= 0 && Count
!= 1; Opts
= Opts
->Next
, Count
--);
125 _error
->Error("Problem executing scripts %s '%s'",Cnf
,Opts
->Value
.c_str());
128 return _error
->Error("Sub-process returned an error code");
135 // CopyFile - Buffered copy of a file /*{{{*/
136 // ---------------------------------------------------------------------
137 /* The caller is expected to set things so that failure causes erasure */
138 bool CopyFile(FileFd
&From
,FileFd
&To
)
140 if (From
.IsOpen() == false || To
.IsOpen() == false)
143 // Buffered copy between fds
144 SPtrArray
<unsigned char> Buf
= new unsigned char[64000];
145 unsigned long long Size
= From
.Size();
148 unsigned long long ToRead
= Size
;
152 if (From
.Read(Buf
,ToRead
) == false ||
153 To
.Write(Buf
,ToRead
) == false)
162 // GetLock - Gets a lock file /*{{{*/
163 // ---------------------------------------------------------------------
164 /* This will create an empty file of the given name and lock it. Once this
165 is done all other calls to GetLock in any other process will fail with
166 -1. The return result is the fd of the file, the call should call
167 close at some time. */
168 int GetLock(string File
,bool Errors
)
170 // GetLock() is used in aptitude on directories with public-write access
171 // Use O_NOFOLLOW here to prevent symlink traversal attacks
172 int FD
= open(File
.c_str(),O_RDWR
| O_CREAT
| O_NOFOLLOW
,0640);
175 // Read only .. cant have locking problems there.
178 _error
->Warning(_("Not using locking for read only lock file %s"),File
.c_str());
179 return dup(0); // Need something for the caller to close
183 _error
->Errno("open",_("Could not open lock file %s"),File
.c_str());
185 // Feh.. We do this to distinguish the lock vs open case..
189 SetCloseExec(FD
,true);
191 // Aquire a write lock
194 fl
.l_whence
= SEEK_SET
;
197 if (fcntl(FD
,F_SETLK
,&fl
) == -1)
201 _error
->Warning(_("Not using locking for nfs mounted lock file %s"),File
.c_str());
202 return dup(0); // Need something for the caller to close
205 _error
->Errno("open",_("Could not get lock %s"),File
.c_str());
216 // FileExists - Check if a file exists /*{{{*/
217 // ---------------------------------------------------------------------
218 /* Beware: Directories are also files! */
219 bool FileExists(string File
)
222 if (stat(File
.c_str(),&Buf
) != 0)
227 // RealFileExists - Check if a file exists and if it is really a file /*{{{*/
228 // ---------------------------------------------------------------------
230 bool RealFileExists(string File
)
233 if (stat(File
.c_str(),&Buf
) != 0)
235 return ((Buf
.st_mode
& S_IFREG
) != 0);
238 // DirectoryExists - Check if a directory exists and is really one /*{{{*/
239 // ---------------------------------------------------------------------
241 bool DirectoryExists(string
const &Path
)
244 if (stat(Path
.c_str(),&Buf
) != 0)
246 return ((Buf
.st_mode
& S_IFDIR
) != 0);
249 // CreateDirectory - poor man's mkdir -p guarded by a parent directory /*{{{*/
250 // ---------------------------------------------------------------------
251 /* This method will create all directories needed for path in good old
252 mkdir -p style but refuses to do this if Parent is not a prefix of
253 this Path. Example: /var/cache/ and /var/cache/apt/archives are given,
254 so it will create apt/archives if /var/cache exists - on the other
255 hand if the parent is /var/lib the creation will fail as this path
256 is not a parent of the path to be generated. */
257 bool CreateDirectory(string
const &Parent
, string
const &Path
)
259 if (Parent
.empty() == true || Path
.empty() == true)
262 if (DirectoryExists(Path
) == true)
265 if (DirectoryExists(Parent
) == false)
268 // we are not going to create directories "into the blue"
269 if (Path
.find(Parent
, 0) != 0)
272 vector
<string
> const dirs
= VectorizeString(Path
.substr(Parent
.size()), '/');
273 string progress
= Parent
;
274 for (vector
<string
>::const_iterator d
= dirs
.begin(); d
!= dirs
.end(); ++d
)
276 if (d
->empty() == true)
279 progress
.append("/").append(*d
);
280 if (DirectoryExists(progress
) == true)
283 if (mkdir(progress
.c_str(), 0755) != 0)
289 // CreateAPTDirectoryIfNeeded - ensure that the given directory exists /*{{{*/
290 // ---------------------------------------------------------------------
291 /* a small wrapper around CreateDirectory to check if it exists and to
292 remove the trailing "/apt/" from the parent directory if needed */
293 bool CreateAPTDirectoryIfNeeded(string
const &Parent
, string
const &Path
)
295 if (DirectoryExists(Path
) == true)
298 size_t const len
= Parent
.size();
299 if (len
> 5 && Parent
.find("/apt/", len
- 6, 5) == len
- 5)
301 if (CreateDirectory(Parent
.substr(0,len
-5), Path
) == true)
304 else if (CreateDirectory(Parent
, Path
) == true)
310 // GetListOfFilesInDir - returns a vector of files in the given dir /*{{{*/
311 // ---------------------------------------------------------------------
312 /* If an extension is given only files with this extension are included
313 in the returned vector, otherwise every "normal" file is included. */
314 std::vector
<string
> GetListOfFilesInDir(string
const &Dir
, string
const &Ext
,
315 bool const &SortList
, bool const &AllowNoExt
)
317 std::vector
<string
> ext
;
319 if (Ext
.empty() == false)
321 if (AllowNoExt
== true && ext
.empty() == false)
323 return GetListOfFilesInDir(Dir
, ext
, SortList
);
325 std::vector
<string
> GetListOfFilesInDir(string
const &Dir
, std::vector
<string
> const &Ext
,
326 bool const &SortList
)
328 // Attention debuggers: need to be set with the environment config file!
329 bool const Debug
= _config
->FindB("Debug::GetListOfFilesInDir", false);
332 std::clog
<< "Accept in " << Dir
<< " only files with the following " << Ext
.size() << " extensions:" << std::endl
;
333 if (Ext
.empty() == true)
334 std::clog
<< "\tNO extension" << std::endl
;
336 for (std::vector
<string
>::const_iterator e
= Ext
.begin();
338 std::clog
<< '\t' << (e
->empty() == true ? "NO" : *e
) << " extension" << std::endl
;
341 std::vector
<string
> List
;
343 if (DirectoryExists(Dir
.c_str()) == false)
345 _error
->Error(_("List of files can't be created as '%s' is not a directory"), Dir
.c_str());
349 Configuration::MatchAgainstConfig
SilentIgnore("Dir::Ignore-Files-Silently");
350 DIR *D
= opendir(Dir
.c_str());
353 _error
->Errno("opendir",_("Unable to read %s"),Dir
.c_str());
357 for (struct dirent
*Ent
= readdir(D
); Ent
!= 0; Ent
= readdir(D
))
359 // skip "hidden" files
360 if (Ent
->d_name
[0] == '.')
363 // Make sure it is a file and not something else
364 string
const File
= flCombine(Dir
,Ent
->d_name
);
365 #ifdef _DIRENT_HAVE_D_TYPE
366 if (Ent
->d_type
!= DT_REG
)
369 if (RealFileExists(File
.c_str()) == false)
371 if (SilentIgnore
.Match(Ent
->d_name
) == false)
372 _error
->Notice(_("Ignoring '%s' in directory '%s' as it is not a regular file"), Ent
->d_name
, Dir
.c_str());
377 // check for accepted extension:
378 // no extension given -> periods are bad as hell!
379 // extensions given -> "" extension allows no extension
380 if (Ext
.empty() == false)
382 string d_ext
= flExtension(Ent
->d_name
);
383 if (d_ext
== Ent
->d_name
) // no extension
385 if (std::find(Ext
.begin(), Ext
.end(), "") == Ext
.end())
388 std::clog
<< "Bad file: " << Ent
->d_name
<< " → no extension" << std::endl
;
389 if (SilentIgnore
.Match(Ent
->d_name
) == false)
390 _error
->Notice(_("Ignoring file '%s' in directory '%s' as it has no filename extension"), Ent
->d_name
, Dir
.c_str());
394 else if (std::find(Ext
.begin(), Ext
.end(), d_ext
) == Ext
.end())
397 std::clog
<< "Bad file: " << Ent
->d_name
<< " → bad extension »" << flExtension(Ent
->d_name
) << "«" << std::endl
;
398 if (SilentIgnore
.Match(Ent
->d_name
) == false)
399 _error
->Notice(_("Ignoring file '%s' in directory '%s' as it has an invalid filename extension"), Ent
->d_name
, Dir
.c_str());
404 // Skip bad filenames ala run-parts
405 const char *C
= Ent
->d_name
;
407 if (isalpha(*C
) == 0 && isdigit(*C
) == 0
408 && *C
!= '_' && *C
!= '-') {
409 // no required extension -> dot is a bad character
410 if (*C
== '.' && Ext
.empty() == false)
415 // we don't reach the end of the name -> bad character included
419 std::clog
<< "Bad file: " << Ent
->d_name
<< " → bad character »"
420 << *C
<< "« in filename (period allowed: " << (Ext
.empty() ? "no" : "yes") << ")" << std::endl
;
424 // skip filenames which end with a period. These are never valid
428 std::clog
<< "Bad file: " << Ent
->d_name
<< " → Period as last character" << std::endl
;
433 std::clog
<< "Accept file: " << Ent
->d_name
<< " in " << Dir
<< std::endl
;
434 List
.push_back(File
);
438 if (SortList
== true)
439 std::sort(List
.begin(),List
.end());
443 // SafeGetCWD - This is a safer getcwd that returns a dynamic string /*{{{*/
444 // ---------------------------------------------------------------------
445 /* We return / on failure. */
448 // Stash the current dir.
451 if (getcwd(S
,sizeof(S
)-2) == 0)
453 unsigned int Len
= strlen(S
);
459 // GetModificationTime - Get the mtime of the given file or -1 on error /*{{{*/
460 // ---------------------------------------------------------------------
461 /* We return / on failure. */
462 time_t GetModificationTime(string
const &Path
)
465 if (stat(Path
.c_str(), &St
) < 0)
470 // flNotDir - Strip the directory from the filename /*{{{*/
471 // ---------------------------------------------------------------------
473 string
flNotDir(string File
)
475 string::size_type Res
= File
.rfind('/');
476 if (Res
== string::npos
)
479 return string(File
,Res
,Res
- File
.length());
482 // flNotFile - Strip the file from the directory name /*{{{*/
483 // ---------------------------------------------------------------------
484 /* Result ends in a / */
485 string
flNotFile(string File
)
487 string::size_type Res
= File
.rfind('/');
488 if (Res
== string::npos
)
491 return string(File
,0,Res
);
494 // flExtension - Return the extension for the file /*{{{*/
495 // ---------------------------------------------------------------------
497 string
flExtension(string File
)
499 string::size_type Res
= File
.rfind('.');
500 if (Res
== string::npos
)
503 return string(File
,Res
,Res
- File
.length());
506 // flNoLink - If file is a symlink then deref it /*{{{*/
507 // ---------------------------------------------------------------------
508 /* If the name is not a link then the returned path is the input. */
509 string
flNoLink(string File
)
512 if (lstat(File
.c_str(),&St
) != 0 || S_ISLNK(St
.st_mode
) == 0)
514 if (stat(File
.c_str(),&St
) != 0)
517 /* Loop resolving the link. There is no need to limit the number of
518 loops because the stat call above ensures that the symlink is not
526 if ((Res
= readlink(NFile
.c_str(),Buffer
,sizeof(Buffer
))) <= 0 ||
527 (unsigned)Res
>= sizeof(Buffer
))
530 // Append or replace the previous path
532 if (Buffer
[0] == '/')
535 NFile
= flNotFile(NFile
) + Buffer
;
537 // See if we are done
538 if (lstat(NFile
.c_str(),&St
) != 0)
540 if (S_ISLNK(St
.st_mode
) == 0)
545 // flCombine - Combine a file and a directory /*{{{*/
546 // ---------------------------------------------------------------------
547 /* If the file is an absolute path then it is just returned, otherwise
548 the directory is pre-pended to it. */
549 string
flCombine(string Dir
,string File
)
551 if (File
.empty() == true)
554 if (File
[0] == '/' || Dir
.empty() == true)
556 if (File
.length() >= 2 && File
[0] == '.' && File
[1] == '/')
558 if (Dir
[Dir
.length()-1] == '/')
560 return Dir
+ '/' + File
;
563 // SetCloseExec - Set the close on exec flag /*{{{*/
564 // ---------------------------------------------------------------------
566 void SetCloseExec(int Fd
,bool Close
)
568 if (fcntl(Fd
,F_SETFD
,(Close
== false)?0:FD_CLOEXEC
) != 0)
570 cerr
<< "FATAL -> Could not set close on exec " << strerror(errno
) << endl
;
575 // SetNonBlock - Set the nonblocking flag /*{{{*/
576 // ---------------------------------------------------------------------
578 void SetNonBlock(int Fd
,bool Block
)
580 int Flags
= fcntl(Fd
,F_GETFL
) & (~O_NONBLOCK
);
581 if (fcntl(Fd
,F_SETFL
,Flags
| ((Block
== false)?0:O_NONBLOCK
)) != 0)
583 cerr
<< "FATAL -> Could not set non-blocking flag " << strerror(errno
) << endl
;
588 // WaitFd - Wait for a FD to become readable /*{{{*/
589 // ---------------------------------------------------------------------
590 /* This waits for a FD to become readable using select. It is useful for
591 applications making use of non-blocking sockets. The timeout is
593 bool WaitFd(int Fd
,bool write
,unsigned long timeout
)
606 Res
= select(Fd
+1,0,&Set
,0,(timeout
!= 0?&tv
:0));
608 while (Res
< 0 && errno
== EINTR
);
618 Res
= select(Fd
+1,&Set
,0,0,(timeout
!= 0?&tv
:0));
620 while (Res
< 0 && errno
== EINTR
);
629 // ExecFork - Magical fork that sanitizes the context before execing /*{{{*/
630 // ---------------------------------------------------------------------
631 /* This is used if you want to cleanse the environment for the forked
632 child, it fixes up the important signals and nukes all of the fds,
633 otherwise acts like normal fork. */
636 // Fork off the process
637 pid_t Process
= fork();
640 cerr
<< "FATAL -> Failed to fork." << endl
;
644 // Spawn the subprocess
648 signal(SIGPIPE
,SIG_DFL
);
649 signal(SIGQUIT
,SIG_DFL
);
650 signal(SIGINT
,SIG_DFL
);
651 signal(SIGWINCH
,SIG_DFL
);
652 signal(SIGCONT
,SIG_DFL
);
653 signal(SIGTSTP
,SIG_DFL
);
656 Configuration::Item
const *Opts
= _config
->Tree("APT::Keep-Fds");
657 if (Opts
!= 0 && Opts
->Child
!= 0)
660 for (; Opts
!= 0; Opts
= Opts
->Next
)
662 if (Opts
->Value
.empty() == true)
664 int fd
= atoi(Opts
->Value
.c_str());
669 // Close all of our FDs - just in case
670 for (int K
= 3; K
!= 40; K
++)
672 if(KeepFDs
.find(K
) == KeepFDs
.end())
673 fcntl(K
,F_SETFD
,FD_CLOEXEC
);
680 // ExecWait - Fancy waitpid /*{{{*/
681 // ---------------------------------------------------------------------
682 /* Waits for the given sub process. If Reap is set then no errors are
683 generated. Otherwise a failed subprocess will generate a proper descriptive
685 bool ExecWait(pid_t Pid
,const char *Name
,bool Reap
)
690 // Wait and collect the error code
692 while (waitpid(Pid
,&Status
,0) != Pid
)
700 return _error
->Error(_("Waited for %s but it wasn't there"),Name
);
704 // Check for an error code.
705 if (WIFEXITED(Status
) == 0 || WEXITSTATUS(Status
) != 0)
709 if (WIFSIGNALED(Status
) != 0)
711 if( WTERMSIG(Status
) == SIGSEGV
)
712 return _error
->Error(_("Sub-process %s received a segmentation fault."),Name
);
714 return _error
->Error(_("Sub-process %s received signal %u."),Name
, WTERMSIG(Status
));
717 if (WIFEXITED(Status
) != 0)
718 return _error
->Error(_("Sub-process %s returned an error code (%u)"),Name
,WEXITSTATUS(Status
));
720 return _error
->Error(_("Sub-process %s exited unexpectedly"),Name
);
727 // FileFd::Open - Open a file /*{{{*/
728 // ---------------------------------------------------------------------
729 /* The most commonly used open mode combinations are given with Mode */
730 bool FileFd::Open(string FileName
,OpenMode Mode
,CompressMode Compress
, unsigned long const Perms
)
732 if (Mode
== ReadOnlyGzip
)
733 return Open(FileName
, ReadOnly
, Gzip
, Perms
);
735 d
= new FileFdPrivate
;
738 if (Compress
== Auto
&& (Mode
& WriteOnly
) == WriteOnly
)
739 return _error
->Error("Autodetection on %s only works in ReadOnly openmode!", FileName
.c_str());
740 if ((Mode
& WriteOnly
) != WriteOnly
&& (Mode
& (Atomic
| Create
| Empty
| Exclusive
)) != 0)
741 return _error
->Error("ReadOnly mode for %s doesn't accept additional flags!", FileName
.c_str());
744 #define if_FLAGGED_SET(FLAG, MODE) if ((Mode & FLAG) == FLAG) fileflags |= MODE
745 if_FLAGGED_SET(ReadWrite
, O_RDWR
);
746 else if_FLAGGED_SET(ReadOnly
, O_RDONLY
);
747 else if_FLAGGED_SET(WriteOnly
, O_WRONLY
);
748 else return _error
->Error("No openmode provided in FileFd::Open for %s", FileName
.c_str());
750 if_FLAGGED_SET(Create
, O_CREAT
);
751 if_FLAGGED_SET(Exclusive
, O_EXCL
);
752 else if_FLAGGED_SET(Atomic
, O_EXCL
);
753 if_FLAGGED_SET(Empty
, O_TRUNC
);
754 #undef if_FLAGGED_SET
756 // FIXME: Denote inbuilt compressors somehow - as we don't need to have the binaries for them
757 std::vector
<APT::Configuration::Compressor
> const compressors
= APT::Configuration::getCompressors();
758 std::vector
<APT::Configuration::Compressor
>::const_iterator compressor
= compressors
.begin();
759 if (Compress
== Auto
)
762 for (; compressor
!= compressors
.end(); ++compressor
)
764 std::string file
= std::string(FileName
).append(compressor
->Extension
);
765 if (FileExists(file
) == false)
768 if (compressor
->Binary
== ".")
771 Compress
= Extension
;
775 else if (Compress
== Extension
)
778 std::string ext
= flExtension(FileName
);
782 for (; compressor
!= compressors
.end(); ++compressor
)
783 if (ext
== compressor
->Extension
)
787 else if (Compress
!= None
)
792 case Gzip
: name
= "gzip"; break;
793 case Bzip2
: name
= "bzip2"; break;
794 case Lzma
: name
= "lzma"; break;
795 case Xz
: name
= "xz"; break;
796 default: return _error
->Error("Can't find a match for specified compressor mode for file %s", FileName
.c_str());
798 for (; compressor
!= compressors
.end(); ++compressor
)
799 if (compressor
->Name
== name
)
801 if (compressor
== compressors
.end() && name
!= "gzip")
802 return _error
->Error("Can't find a configured compressor %s for file %s", name
.c_str(), FileName
.c_str());
805 // if we have them, use inbuilt compressors instead of forking
806 if (compressor
!= compressors
.end())
808 if (compressor
->Name
== "gzip")
811 compressor
= compressors
.end();
813 else if (compressor
->Name
== "." || Compress
== None
)
816 compressor
= compressors
.end();
820 if ((Mode
& Atomic
) == Atomic
)
823 char *name
= strdup((FileName
+ ".XXXXXX").c_str());
824 TemporaryFileName
= string(mktemp(name
));
827 else if ((Mode
& (Exclusive
| Create
)) == (Exclusive
| Create
))
829 // for atomic, this will be done by rename in Close()
830 unlink(FileName
.c_str());
832 if ((Mode
& Empty
) == Empty
)
835 if (lstat(FileName
.c_str(),&Buf
) == 0 && S_ISLNK(Buf
.st_mode
))
836 unlink(FileName
.c_str());
839 if (compressor
!= compressors
.end())
841 if ((Mode
& ReadWrite
) == ReadWrite
)
842 _error
->Error("External compressors like %s do not support readwrite mode for file %s", compressor
->Name
.c_str(), FileName
.c_str());
844 _error
->Error("Forking external compressor %s is not implemented for %s", compressor
->Name
.c_str(), FileName
.c_str());
848 if (TemporaryFileName
.empty() == false)
849 iFd
= open(TemporaryFileName
.c_str(), fileflags
, Perms
);
851 iFd
= open(FileName
.c_str(), fileflags
, Perms
);
855 if (OpenInternDescriptor(Mode
, Compress
) == false)
864 return _error
->Errno("open",_("Could not open file %s"),FileName
.c_str());
866 this->FileName
= FileName
;
867 SetCloseExec(iFd
,true);
871 // FileFd::OpenDescriptor - Open a filedescriptor /*{{{*/
872 // ---------------------------------------------------------------------
874 bool FileFd::OpenDescriptor(int Fd
, OpenMode Mode
, CompressMode Compress
, bool AutoClose
)
877 d
= new FileFdPrivate
;
878 Flags
= (AutoClose
) ? FileFd::AutoClose
: 0;
880 if (OpenInternDescriptor(Mode
, Compress
) == false)
884 return _error
->Errno("gzdopen",_("Could not open file descriptor %d"), Fd
);
889 bool FileFd::OpenInternDescriptor(OpenMode Mode
, CompressMode Compress
)
891 if (Compress
== None
)
893 else if (Compress
== Gzip
)
895 if ((Mode
& ReadWrite
) == ReadWrite
)
896 d
->gz
= gzdopen(iFd
, "r+");
897 else if ((Mode
& WriteOnly
) == WriteOnly
)
898 d
->gz
= gzdopen(iFd
, "w");
900 d
->gz
= gzdopen (iFd
, "r");
910 // FileFd::~File - Closes the file /*{{{*/
911 // ---------------------------------------------------------------------
912 /* If the proper modes are selected then we close the Fd and possibly
913 unlink the file on error. */
919 // FileFd::Read - Read a bit of the file /*{{{*/
920 // ---------------------------------------------------------------------
921 /* We are carefull to handle interruption by a signal while reading
923 bool FileFd::Read(void *To
,unsigned long long Size
,unsigned long long *Actual
)
933 Res
= gzread(d
->gz
,To
,Size
);
935 Res
= read(iFd
,To
,Size
);
936 if (Res
< 0 && errno
== EINTR
)
941 return _error
->Errno("read",_("Read error"));
944 To
= (char *)To
+ Res
;
949 while (Res
> 0 && Size
> 0);
962 return _error
->Error(_("read, still have %llu to read but none left"), Size
);
965 // FileFd::ReadLine - Read a complete line from the file /*{{{*/
966 // ---------------------------------------------------------------------
967 /* Beware: This method can be quiet slow for big buffers on UNcompressed
968 files because of the naive implementation! */
969 char* FileFd::ReadLine(char *To
, unsigned long long const Size
)
972 return gzgets(d
->gz
, To
, Size
);
974 unsigned long long read
= 0;
975 if (Read(To
, Size
, &read
) == false)
978 for (; *c
!= '\n' && *c
!= '\0' && read
!= 0; --read
, ++c
)
979 ; // find the end of the line
987 // FileFd::Write - Write to the file /*{{{*/
988 // ---------------------------------------------------------------------
990 bool FileFd::Write(const void *From
,unsigned long long Size
)
997 Res
= gzwrite(d
->gz
,From
,Size
);
999 Res
= write(iFd
,From
,Size
);
1000 if (Res
< 0 && errno
== EINTR
)
1005 return _error
->Errno("write",_("Write error"));
1008 From
= (char *)From
+ Res
;
1011 while (Res
> 0 && Size
> 0);
1017 return _error
->Error(_("write, still have %llu to write but couldn't"), Size
);
1020 // FileFd::Seek - Seek in the file /*{{{*/
1021 // ---------------------------------------------------------------------
1023 bool FileFd::Seek(unsigned long long To
)
1027 res
= gzseek(d
->gz
,To
,SEEK_SET
);
1029 res
= lseek(iFd
,To
,SEEK_SET
);
1030 if (res
!= (signed)To
)
1033 return _error
->Error("Unable to seek to %llu", To
);
1039 // FileFd::Skip - Seek in the file /*{{{*/
1040 // ---------------------------------------------------------------------
1042 bool FileFd::Skip(unsigned long long Over
)
1046 res
= gzseek(d
->gz
,Over
,SEEK_CUR
);
1048 res
= lseek(iFd
,Over
,SEEK_CUR
);
1052 return _error
->Error("Unable to seek ahead %llu",Over
);
1058 // FileFd::Truncate - Truncate the file /*{{{*/
1059 // ---------------------------------------------------------------------
1061 bool FileFd::Truncate(unsigned long long To
)
1066 return _error
->Error("Truncating gzipped files is not implemented (%s)", FileName
.c_str());
1068 if (ftruncate(iFd
,To
) != 0)
1071 return _error
->Error("Unable to truncate to %llu",To
);
1077 // FileFd::Tell - Current seek position /*{{{*/
1078 // ---------------------------------------------------------------------
1080 unsigned long long FileFd::Tell()
1084 Res
= gztell(d
->gz
);
1086 Res
= lseek(iFd
,0,SEEK_CUR
);
1087 if (Res
== (off_t
)-1)
1088 _error
->Errno("lseek","Failed to determine the current file position");
1092 // FileFd::FileSize - Return the size of the file /*{{{*/
1093 // ---------------------------------------------------------------------
1095 unsigned long long FileFd::FileSize()
1099 if (fstat(iFd
,&Buf
) != 0)
1100 return _error
->Errno("fstat","Unable to determine the file size");
1104 // FileFd::Size - Return the size of the content in the file /*{{{*/
1105 // ---------------------------------------------------------------------
1107 unsigned long long FileFd::Size()
1109 unsigned long long size
= FileSize();
1111 // only check gzsize if we are actually a gzip file, just checking for
1112 // "gz" is not sufficient as uncompressed files could be opened with
1113 // gzopen in "direct" mode as well
1114 if (d
->gz
&& !gzdirect(d
->gz
) && size
> 0)
1116 /* unfortunately zlib.h doesn't provide a gzsize(), so we have to do
1117 * this ourselves; the original (uncompressed) file size is the last 32
1118 * bits of the file */
1119 // FIXME: Size for gz-files is limited by 32bit… no largefile support
1120 off_t orig_pos
= lseek(iFd
, 0, SEEK_CUR
);
1121 if (lseek(iFd
, -4, SEEK_END
) < 0)
1122 return _error
->Errno("lseek","Unable to seek to end of gzipped file");
1124 if (read(iFd
, &size
, 4) != 4)
1125 return _error
->Errno("read","Unable to read original size of gzipped file");
1127 #ifdef WORDS_BIGENDIAN
1128 uint32_t tmp_size
= size
;
1129 uint8_t const * const p
= (uint8_t const * const) &tmp_size
;
1130 tmp_size
= (p
[3] << 24) | (p
[2] << 16) | (p
[1] << 8) | p
[0];
1134 if (lseek(iFd
, orig_pos
, SEEK_SET
) < 0)
1135 return _error
->Errno("lseek","Unable to seek in gzipped file");
1142 // FileFd::ModificationTime - Return the time of last touch /*{{{*/
1143 // ---------------------------------------------------------------------
1145 time_t FileFd::ModificationTime()
1148 if (fstat(iFd
,&Buf
) != 0)
1150 _error
->Errno("fstat","Unable to determine the modification time of file %s", FileName
.c_str());
1153 return Buf
.st_mtime
;
1156 // FileFd::Close - Close the file if the close flag is set /*{{{*/
1157 // ---------------------------------------------------------------------
1159 bool FileFd::Close()
1165 if ((Flags
& AutoClose
) == AutoClose
)
1167 if (d
!= NULL
&& d
->gz
!= NULL
) {
1168 int const e
= gzclose(d
->gz
);
1169 // gzdopen() on empty files always fails with "buffer error" here, ignore that
1170 if (e
!= 0 && e
!= Z_BUF_ERROR
)
1171 Res
&= _error
->Errno("close",_("Problem closing the gzip file %s"), FileName
.c_str());
1173 if (iFd
> 0 && close(iFd
) != 0)
1174 Res
&= _error
->Errno("close",_("Problem closing the file %s"), FileName
.c_str());
1177 if ((Flags
& Replace
) == Replace
&& iFd
>= 0) {
1178 if (rename(TemporaryFileName
.c_str(), FileName
.c_str()) != 0)
1179 Res
&= _error
->Errno("rename",_("Problem renaming the file %s to %s"), TemporaryFileName
.c_str(), FileName
.c_str());
1181 FileName
= TemporaryFileName
; // for the unlink() below.
1182 TemporaryFileName
.clear();
1187 if ((Flags
& Fail
) == Fail
&& (Flags
& DelOnFail
) == DelOnFail
&&
1188 FileName
.empty() == false)
1189 if (unlink(FileName
.c_str()) != 0)
1190 Res
&= _error
->WarningE("unlnk",_("Problem unlinking the file %s"), FileName
.c_str());
1201 // FileFd::Sync - Sync the file /*{{{*/
1202 // ---------------------------------------------------------------------
1206 #ifdef _POSIX_SYNCHRONIZED_IO
1207 if (fsync(iFd
) != 0)
1208 return _error
->Errno("sync",_("Problem syncing the file"));
1213 gzFile
FileFd::gzFd() {return d
->gz
;};