]> git.saurik.com Git - apt.git/blame_incremental - apt-pkg/contrib/fileutl.cc
test-indexes: Use /etc/apt from temporary work dir, not from system
[apt.git] / apt-pkg / contrib / fileutl.cc
... / ...
CommitLineData
1// -*- mode: cpp; mode: fold -*-
2// Description /*{{{*/
3// $Id: fileutl.cc,v 1.42 2002/09/14 05:29:22 jgg Exp $
4/* ######################################################################
5
6 File Utilities
7
8 CopyFile - Buffered copy of a single file
9 GetLock - dpkg compatible lock file manipulation (fcntl)
10
11 Most of this source is placed in the Public Domain, do with it what
12 you will
13 It was originally written by Jason Gunthorpe <jgg@debian.org>.
14 FileFd gzip support added by Martin Pitt <martin.pitt@canonical.com>
15
16 The exception is RunScripts() it is under the GPLv2
17
18 ##################################################################### */
19 /*}}}*/
20// Include Files /*{{{*/
21#include <apt-pkg/fileutl.h>
22#include <apt-pkg/error.h>
23#include <apt-pkg/sptr.h>
24#include <apt-pkg/configuration.h>
25
26#include <apti18n.h>
27
28#include <cstdlib>
29#include <cstring>
30
31#include <iostream>
32#include <unistd.h>
33#include <fcntl.h>
34#include <sys/stat.h>
35#include <sys/types.h>
36#include <sys/time.h>
37#include <sys/wait.h>
38#include <dirent.h>
39#include <signal.h>
40#include <errno.h>
41#include <set>
42#include <algorithm>
43 /*}}}*/
44
45using namespace std;
46
47// RunScripts - Run a set of scripts from a configuration subtree /*{{{*/
48// ---------------------------------------------------------------------
49/* */
50bool RunScripts(const char *Cnf)
51{
52 Configuration::Item const *Opts = _config->Tree(Cnf);
53 if (Opts == 0 || Opts->Child == 0)
54 return true;
55 Opts = Opts->Child;
56
57 // Fork for running the system calls
58 pid_t Child = ExecFork();
59
60 // This is the child
61 if (Child == 0)
62 {
63 if (chdir("/tmp/") != 0)
64 _exit(100);
65
66 unsigned int Count = 1;
67 for (; Opts != 0; Opts = Opts->Next, Count++)
68 {
69 if (Opts->Value.empty() == true)
70 continue;
71
72 if (system(Opts->Value.c_str()) != 0)
73 _exit(100+Count);
74 }
75 _exit(0);
76 }
77
78 // Wait for the child
79 int Status = 0;
80 while (waitpid(Child,&Status,0) != Child)
81 {
82 if (errno == EINTR)
83 continue;
84 return _error->Errno("waitpid","Couldn't wait for subprocess");
85 }
86
87 // Restore sig int/quit
88 signal(SIGQUIT,SIG_DFL);
89 signal(SIGINT,SIG_DFL);
90
91 // Check for an error code.
92 if (WIFEXITED(Status) == 0 || WEXITSTATUS(Status) != 0)
93 {
94 unsigned int Count = WEXITSTATUS(Status);
95 if (Count > 100)
96 {
97 Count -= 100;
98 for (; Opts != 0 && Count != 1; Opts = Opts->Next, Count--);
99 _error->Error("Problem executing scripts %s '%s'",Cnf,Opts->Value.c_str());
100 }
101
102 return _error->Error("Sub-process returned an error code");
103 }
104
105 return true;
106}
107 /*}}}*/
108
109// CopyFile - Buffered copy of a file /*{{{*/
110// ---------------------------------------------------------------------
111/* The caller is expected to set things so that failure causes erasure */
112bool CopyFile(FileFd &From,FileFd &To)
113{
114 if (From.IsOpen() == false || To.IsOpen() == false)
115 return false;
116
117 // Buffered copy between fds
118 SPtrArray<unsigned char> Buf = new unsigned char[64000];
119 unsigned long Size = From.Size();
120 while (Size != 0)
121 {
122 unsigned long ToRead = Size;
123 if (Size > 64000)
124 ToRead = 64000;
125
126 if (From.Read(Buf,ToRead) == false ||
127 To.Write(Buf,ToRead) == false)
128 return false;
129
130 Size -= ToRead;
131 }
132
133 return true;
134}
135 /*}}}*/
136// GetLock - Gets a lock file /*{{{*/
137// ---------------------------------------------------------------------
138/* This will create an empty file of the given name and lock it. Once this
139 is done all other calls to GetLock in any other process will fail with
140 -1. The return result is the fd of the file, the call should call
141 close at some time. */
142int GetLock(string File,bool Errors)
143{
144 // GetLock() is used in aptitude on directories with public-write access
145 // Use O_NOFOLLOW here to prevent symlink traversal attacks
146 int FD = open(File.c_str(),O_RDWR | O_CREAT | O_NOFOLLOW,0640);
147 if (FD < 0)
148 {
149 // Read only .. cant have locking problems there.
150 if (errno == EROFS)
151 {
152 _error->Warning(_("Not using locking for read only lock file %s"),File.c_str());
153 return dup(0); // Need something for the caller to close
154 }
155
156 if (Errors == true)
157 _error->Errno("open",_("Could not open lock file %s"),File.c_str());
158
159 // Feh.. We do this to distinguish the lock vs open case..
160 errno = EPERM;
161 return -1;
162 }
163 SetCloseExec(FD,true);
164
165 // Aquire a write lock
166 struct flock fl;
167 fl.l_type = F_WRLCK;
168 fl.l_whence = SEEK_SET;
169 fl.l_start = 0;
170 fl.l_len = 0;
171 if (fcntl(FD,F_SETLK,&fl) == -1)
172 {
173 if (errno == ENOLCK)
174 {
175 _error->Warning(_("Not using locking for nfs mounted lock file %s"),File.c_str());
176 return dup(0); // Need something for the caller to close
177 }
178 if (Errors == true)
179 _error->Errno("open",_("Could not get lock %s"),File.c_str());
180
181 int Tmp = errno;
182 close(FD);
183 errno = Tmp;
184 return -1;
185 }
186
187 return FD;
188}
189 /*}}}*/
190// FileExists - Check if a file exists /*{{{*/
191// ---------------------------------------------------------------------
192/* */
193bool FileExists(string File)
194{
195 struct stat Buf;
196 if (stat(File.c_str(),&Buf) != 0)
197 return false;
198 return true;
199}
200 /*}}}*/
201// GetListOfFilesInDir - returns a vector of files in the given dir /*{{{*/
202// ---------------------------------------------------------------------
203/* If an extension is given only files with this extension are included
204 in the returned vector, otherwise every "normal" file is included. */
205std::vector<string> GetListOfFilesInDir(string const &Dir, string const &Ext,
206 bool const &SortList)
207{
208 return GetListOfFilesInDir(Dir, Ext, SortList, false);
209}
210std::vector<string> GetListOfFilesInDir(string const &Dir, string const &Ext,
211 bool const &SortList, bool const &AllowNoExt)
212{
213 std::vector<string> ext;
214 ext.reserve(2);
215 if (Ext.empty() == false)
216 ext.push_back(Ext);
217 if (AllowNoExt == true && ext.empty() == false)
218 ext.push_back("");
219 return GetListOfFilesInDir(Dir, ext, SortList);
220}
221std::vector<string> GetListOfFilesInDir(string const &Dir, std::vector<string> const &Ext,
222 bool const &SortList)
223{
224 // Attention debuggers: need to be set with the environment config file!
225 bool const Debug = _config->FindB("Debug::GetListOfFilesInDir", false);
226 if (Debug == true)
227 {
228 std::clog << "Accept in " << Dir << " only files with the following " << Ext.size() << " extensions:" << std::endl;
229 if (Ext.empty() == true)
230 std::clog << "\tNO extension" << std::endl;
231 else
232 for (std::vector<string>::const_iterator e = Ext.begin();
233 e != Ext.end(); ++e)
234 std::clog << '\t' << (e->empty() == true ? "NO" : *e) << " extension" << std::endl;
235 }
236
237 std::vector<string> List;
238 DIR *D = opendir(Dir.c_str());
239 if (D == 0)
240 {
241 _error->Errno("opendir",_("Unable to read %s"),Dir.c_str());
242 return List;
243 }
244
245 for (struct dirent *Ent = readdir(D); Ent != 0; Ent = readdir(D))
246 {
247 // skip "hidden" files
248 if (Ent->d_name[0] == '.')
249 continue;
250
251 // check for accepted extension:
252 // no extension given -> periods are bad as hell!
253 // extensions given -> "" extension allows no extension
254 if (Ext.empty() == false)
255 {
256 string d_ext = flExtension(Ent->d_name);
257 if (d_ext == Ent->d_name) // no extension
258 {
259 if (std::find(Ext.begin(), Ext.end(), "") == Ext.end())
260 {
261 if (Debug == true)
262 std::clog << "Bad file: " << Ent->d_name << " → no extension" << std::endl;
263 continue;
264 }
265 }
266 else if (std::find(Ext.begin(), Ext.end(), d_ext) == Ext.end())
267 {
268 if (Debug == true)
269 std::clog << "Bad file: " << Ent->d_name << " → bad extension »" << flExtension(Ent->d_name) << "«" << std::endl;
270 continue;
271 }
272 }
273
274 // Skip bad filenames ala run-parts
275 const char *C = Ent->d_name;
276 for (; *C != 0; ++C)
277 if (isalpha(*C) == 0 && isdigit(*C) == 0
278 && *C != '_' && *C != '-') {
279 // no required extension -> dot is a bad character
280 if (*C == '.' && Ext.empty() == false)
281 continue;
282 break;
283 }
284
285 // we don't reach the end of the name -> bad character included
286 if (*C != 0)
287 {
288 if (Debug == true)
289 std::clog << "Bad file: " << Ent->d_name << " → bad character »"
290 << *C << "« in filename (period allowed: " << (Ext.empty() ? "no" : "yes") << ")" << std::endl;
291 continue;
292 }
293
294 // skip filenames which end with a period. These are never valid
295 if (*(C - 1) == '.')
296 {
297 if (Debug == true)
298 std::clog << "Bad file: " << Ent->d_name << " → Period as last character" << std::endl;
299 continue;
300 }
301
302 // Make sure it is a file and not something else
303 string const File = flCombine(Dir,Ent->d_name);
304 struct stat St;
305 if (stat(File.c_str(),&St) != 0 || S_ISREG(St.st_mode) == 0)
306 {
307 if (Debug == true)
308 std::clog << "Bad file: " << Ent->d_name << " → stat says not a good file" << std::endl;
309 continue;
310 }
311
312 if (Debug == true)
313 std::clog << "Accept file: " << Ent->d_name << " in " << Dir << std::endl;
314 List.push_back(File);
315 }
316 closedir(D);
317
318 if (SortList == true)
319 std::sort(List.begin(),List.end());
320 return List;
321}
322 /*}}}*/
323// SafeGetCWD - This is a safer getcwd that returns a dynamic string /*{{{*/
324// ---------------------------------------------------------------------
325/* We return / on failure. */
326string SafeGetCWD()
327{
328 // Stash the current dir.
329 char S[300];
330 S[0] = 0;
331 if (getcwd(S,sizeof(S)-2) == 0)
332 return "/";
333 unsigned int Len = strlen(S);
334 S[Len] = '/';
335 S[Len+1] = 0;
336 return S;
337}
338 /*}}}*/
339// flNotDir - Strip the directory from the filename /*{{{*/
340// ---------------------------------------------------------------------
341/* */
342string flNotDir(string File)
343{
344 string::size_type Res = File.rfind('/');
345 if (Res == string::npos)
346 return File;
347 Res++;
348 return string(File,Res,Res - File.length());
349}
350 /*}}}*/
351// flNotFile - Strip the file from the directory name /*{{{*/
352// ---------------------------------------------------------------------
353/* Result ends in a / */
354string flNotFile(string File)
355{
356 string::size_type Res = File.rfind('/');
357 if (Res == string::npos)
358 return "./";
359 Res++;
360 return string(File,0,Res);
361}
362 /*}}}*/
363// flExtension - Return the extension for the file /*{{{*/
364// ---------------------------------------------------------------------
365/* */
366string flExtension(string File)
367{
368 string::size_type Res = File.rfind('.');
369 if (Res == string::npos)
370 return File;
371 Res++;
372 return string(File,Res,Res - File.length());
373}
374 /*}}}*/
375// flNoLink - If file is a symlink then deref it /*{{{*/
376// ---------------------------------------------------------------------
377/* If the name is not a link then the returned path is the input. */
378string flNoLink(string File)
379{
380 struct stat St;
381 if (lstat(File.c_str(),&St) != 0 || S_ISLNK(St.st_mode) == 0)
382 return File;
383 if (stat(File.c_str(),&St) != 0)
384 return File;
385
386 /* Loop resolving the link. There is no need to limit the number of
387 loops because the stat call above ensures that the symlink is not
388 circular */
389 char Buffer[1024];
390 string NFile = File;
391 while (1)
392 {
393 // Read the link
394 int Res;
395 if ((Res = readlink(NFile.c_str(),Buffer,sizeof(Buffer))) <= 0 ||
396 (unsigned)Res >= sizeof(Buffer))
397 return File;
398
399 // Append or replace the previous path
400 Buffer[Res] = 0;
401 if (Buffer[0] == '/')
402 NFile = Buffer;
403 else
404 NFile = flNotFile(NFile) + Buffer;
405
406 // See if we are done
407 if (lstat(NFile.c_str(),&St) != 0)
408 return File;
409 if (S_ISLNK(St.st_mode) == 0)
410 return NFile;
411 }
412}
413 /*}}}*/
414// flCombine - Combine a file and a directory /*{{{*/
415// ---------------------------------------------------------------------
416/* If the file is an absolute path then it is just returned, otherwise
417 the directory is pre-pended to it. */
418string flCombine(string Dir,string File)
419{
420 if (File.empty() == true)
421 return string();
422
423 if (File[0] == '/' || Dir.empty() == true)
424 return File;
425 if (File.length() >= 2 && File[0] == '.' && File[1] == '/')
426 return File;
427 if (Dir[Dir.length()-1] == '/')
428 return Dir + File;
429 return Dir + '/' + File;
430}
431 /*}}}*/
432// SetCloseExec - Set the close on exec flag /*{{{*/
433// ---------------------------------------------------------------------
434/* */
435void SetCloseExec(int Fd,bool Close)
436{
437 if (fcntl(Fd,F_SETFD,(Close == false)?0:FD_CLOEXEC) != 0)
438 {
439 cerr << "FATAL -> Could not set close on exec " << strerror(errno) << endl;
440 exit(100);
441 }
442}
443 /*}}}*/
444// SetNonBlock - Set the nonblocking flag /*{{{*/
445// ---------------------------------------------------------------------
446/* */
447void SetNonBlock(int Fd,bool Block)
448{
449 int Flags = fcntl(Fd,F_GETFL) & (~O_NONBLOCK);
450 if (fcntl(Fd,F_SETFL,Flags | ((Block == false)?0:O_NONBLOCK)) != 0)
451 {
452 cerr << "FATAL -> Could not set non-blocking flag " << strerror(errno) << endl;
453 exit(100);
454 }
455}
456 /*}}}*/
457// WaitFd - Wait for a FD to become readable /*{{{*/
458// ---------------------------------------------------------------------
459/* This waits for a FD to become readable using select. It is useful for
460 applications making use of non-blocking sockets. The timeout is
461 in seconds. */
462bool WaitFd(int Fd,bool write,unsigned long timeout)
463{
464 fd_set Set;
465 struct timeval tv;
466 FD_ZERO(&Set);
467 FD_SET(Fd,&Set);
468 tv.tv_sec = timeout;
469 tv.tv_usec = 0;
470 if (write == true)
471 {
472 int Res;
473 do
474 {
475 Res = select(Fd+1,0,&Set,0,(timeout != 0?&tv:0));
476 }
477 while (Res < 0 && errno == EINTR);
478
479 if (Res <= 0)
480 return false;
481 }
482 else
483 {
484 int Res;
485 do
486 {
487 Res = select(Fd+1,&Set,0,0,(timeout != 0?&tv:0));
488 }
489 while (Res < 0 && errno == EINTR);
490
491 if (Res <= 0)
492 return false;
493 }
494
495 return true;
496}
497 /*}}}*/
498// ExecFork - Magical fork that sanitizes the context before execing /*{{{*/
499// ---------------------------------------------------------------------
500/* This is used if you want to cleanse the environment for the forked
501 child, it fixes up the important signals and nukes all of the fds,
502 otherwise acts like normal fork. */
503pid_t ExecFork()
504{
505 // Fork off the process
506 pid_t Process = fork();
507 if (Process < 0)
508 {
509 cerr << "FATAL -> Failed to fork." << endl;
510 exit(100);
511 }
512
513 // Spawn the subprocess
514 if (Process == 0)
515 {
516 // Setup the signals
517 signal(SIGPIPE,SIG_DFL);
518 signal(SIGQUIT,SIG_DFL);
519 signal(SIGINT,SIG_DFL);
520 signal(SIGWINCH,SIG_DFL);
521 signal(SIGCONT,SIG_DFL);
522 signal(SIGTSTP,SIG_DFL);
523
524 set<int> KeepFDs;
525 Configuration::Item const *Opts = _config->Tree("APT::Keep-Fds");
526 if (Opts != 0 && Opts->Child != 0)
527 {
528 Opts = Opts->Child;
529 for (; Opts != 0; Opts = Opts->Next)
530 {
531 if (Opts->Value.empty() == true)
532 continue;
533 int fd = atoi(Opts->Value.c_str());
534 KeepFDs.insert(fd);
535 }
536 }
537
538 // Close all of our FDs - just in case
539 for (int K = 3; K != 40; K++)
540 {
541 if(KeepFDs.find(K) == KeepFDs.end())
542 fcntl(K,F_SETFD,FD_CLOEXEC);
543 }
544 }
545
546 return Process;
547}
548 /*}}}*/
549// ExecWait - Fancy waitpid /*{{{*/
550// ---------------------------------------------------------------------
551/* Waits for the given sub process. If Reap is set then no errors are
552 generated. Otherwise a failed subprocess will generate a proper descriptive
553 message */
554bool ExecWait(pid_t Pid,const char *Name,bool Reap)
555{
556 if (Pid <= 1)
557 return true;
558
559 // Wait and collect the error code
560 int Status;
561 while (waitpid(Pid,&Status,0) != Pid)
562 {
563 if (errno == EINTR)
564 continue;
565
566 if (Reap == true)
567 return false;
568
569 return _error->Error(_("Waited for %s but it wasn't there"),Name);
570 }
571
572
573 // Check for an error code.
574 if (WIFEXITED(Status) == 0 || WEXITSTATUS(Status) != 0)
575 {
576 if (Reap == true)
577 return false;
578 if (WIFSIGNALED(Status) != 0)
579 {
580 if( WTERMSIG(Status) == SIGSEGV)
581 return _error->Error(_("Sub-process %s received a segmentation fault."),Name);
582 else
583 return _error->Error(_("Sub-process %s received signal %u."),Name, WTERMSIG(Status));
584 }
585
586 if (WIFEXITED(Status) != 0)
587 return _error->Error(_("Sub-process %s returned an error code (%u)"),Name,WEXITSTATUS(Status));
588
589 return _error->Error(_("Sub-process %s exited unexpectedly"),Name);
590 }
591
592 return true;
593}
594 /*}}}*/
595
596// FileFd::Open - Open a file /*{{{*/
597// ---------------------------------------------------------------------
598/* The most commonly used open mode combinations are given with Mode */
599bool FileFd::Open(string FileName,OpenMode Mode, unsigned long Perms)
600{
601 Close();
602 Flags = AutoClose;
603 switch (Mode)
604 {
605 case ReadOnly:
606 iFd = open(FileName.c_str(),O_RDONLY);
607 if (iFd > 0 && FileName.compare(FileName.size()-3, 3, ".gz") == 0) {
608 gz = gzdopen (iFd, "r");
609 if (gz == NULL) {
610 close (iFd);
611 iFd = -1;
612 }
613 }
614 break;
615
616 case WriteEmpty:
617 {
618 struct stat Buf;
619 if (lstat(FileName.c_str(),&Buf) == 0 && S_ISLNK(Buf.st_mode))
620 unlink(FileName.c_str());
621 iFd = open(FileName.c_str(),O_RDWR | O_CREAT | O_TRUNC,Perms);
622 break;
623 }
624
625 case WriteExists:
626 iFd = open(FileName.c_str(),O_RDWR);
627 break;
628
629 case WriteAny:
630 iFd = open(FileName.c_str(),O_RDWR | O_CREAT,Perms);
631 break;
632
633 case WriteTemp:
634 unlink(FileName.c_str());
635 iFd = open(FileName.c_str(),O_RDWR | O_CREAT | O_EXCL,Perms);
636 break;
637 }
638
639 if (iFd < 0)
640 return _error->Errno("open",_("Could not open file %s"),FileName.c_str());
641
642 this->FileName = FileName;
643 SetCloseExec(iFd,true);
644 return true;
645}
646 /*}}}*/
647// FileFd::~File - Closes the file /*{{{*/
648// ---------------------------------------------------------------------
649/* If the proper modes are selected then we close the Fd and possibly
650 unlink the file on error. */
651FileFd::~FileFd()
652{
653 Close();
654}
655 /*}}}*/
656// FileFd::Read - Read a bit of the file /*{{{*/
657// ---------------------------------------------------------------------
658/* We are carefull to handle interruption by a signal while reading
659 gracefully. */
660bool FileFd::Read(void *To,unsigned long Size,unsigned long *Actual)
661{
662 int Res;
663 errno = 0;
664 if (Actual != 0)
665 *Actual = 0;
666
667 do
668 {
669 if (gz != NULL)
670 Res = gzread(gz,To,Size);
671 else
672 Res = read(iFd,To,Size);
673 if (Res < 0 && errno == EINTR)
674 continue;
675 if (Res < 0)
676 {
677 Flags |= Fail;
678 return _error->Errno("read",_("Read error"));
679 }
680
681 To = (char *)To + Res;
682 Size -= Res;
683 if (Actual != 0)
684 *Actual += Res;
685 }
686 while (Res > 0 && Size > 0);
687
688 if (Size == 0)
689 return true;
690
691 // Eof handling
692 if (Actual != 0)
693 {
694 Flags |= HitEof;
695 return true;
696 }
697
698 Flags |= Fail;
699 return _error->Error(_("read, still have %lu to read but none left"),Size);
700}
701 /*}}}*/
702// FileFd::Write - Write to the file /*{{{*/
703// ---------------------------------------------------------------------
704/* */
705bool FileFd::Write(const void *From,unsigned long Size)
706{
707 int Res;
708 errno = 0;
709 do
710 {
711 if (gz != NULL)
712 Res = gzwrite(gz,From,Size);
713 else
714 Res = write(iFd,From,Size);
715 if (Res < 0 && errno == EINTR)
716 continue;
717 if (Res < 0)
718 {
719 Flags |= Fail;
720 return _error->Errno("write",_("Write error"));
721 }
722
723 From = (char *)From + Res;
724 Size -= Res;
725 }
726 while (Res > 0 && Size > 0);
727
728 if (Size == 0)
729 return true;
730
731 Flags |= Fail;
732 return _error->Error(_("write, still have %lu to write but couldn't"),Size);
733}
734 /*}}}*/
735// FileFd::Seek - Seek in the file /*{{{*/
736// ---------------------------------------------------------------------
737/* */
738bool FileFd::Seek(unsigned long To)
739{
740 int res;
741 if (gz)
742 res = gzseek(gz,To,SEEK_SET);
743 else
744 res = lseek(iFd,To,SEEK_SET);
745 if (res != (signed)To)
746 {
747 Flags |= Fail;
748 return _error->Error("Unable to seek to %lu",To);
749 }
750
751 return true;
752}
753 /*}}}*/
754// FileFd::Skip - Seek in the file /*{{{*/
755// ---------------------------------------------------------------------
756/* */
757bool FileFd::Skip(unsigned long Over)
758{
759 int res;
760 if (gz)
761 res = gzseek(gz,Over,SEEK_CUR);
762 else
763 res = lseek(iFd,Over,SEEK_CUR);
764 if (res < 0)
765 {
766 Flags |= Fail;
767 return _error->Error("Unable to seek ahead %lu",Over);
768 }
769
770 return true;
771}
772 /*}}}*/
773// FileFd::Truncate - Truncate the file /*{{{*/
774// ---------------------------------------------------------------------
775/* */
776bool FileFd::Truncate(unsigned long To)
777{
778 if (gz)
779 {
780 Flags |= Fail;
781 return _error->Error("Truncating gzipped files is not implemented (%s)", FileName.c_str());
782 }
783 if (ftruncate(iFd,To) != 0)
784 {
785 Flags |= Fail;
786 return _error->Error("Unable to truncate to %lu",To);
787 }
788
789 return true;
790}
791 /*}}}*/
792// FileFd::Tell - Current seek position /*{{{*/
793// ---------------------------------------------------------------------
794/* */
795unsigned long FileFd::Tell()
796{
797 off_t Res;
798 if (gz)
799 Res = gztell(gz);
800 else
801 Res = lseek(iFd,0,SEEK_CUR);
802 if (Res == (off_t)-1)
803 _error->Errno("lseek","Failed to determine the current file position");
804 return Res;
805}
806 /*}}}*/
807// FileFd::Size - Return the size of the file /*{{{*/
808// ---------------------------------------------------------------------
809/* */
810unsigned long FileFd::Size()
811{
812 //TODO: For gz, do we need the actual file size here or the uncompressed length?
813 struct stat Buf;
814 if (fstat(iFd,&Buf) != 0)
815 return _error->Errno("fstat","Unable to determine the file size");
816 return Buf.st_size;
817}
818 /*}}}*/
819// FileFd::Close - Close the file if the close flag is set /*{{{*/
820// ---------------------------------------------------------------------
821/* */
822bool FileFd::Close()
823{
824 bool Res = true;
825 if ((Flags & AutoClose) == AutoClose)
826 if ((gz != NULL && gzclose(gz) != 0) || (gz == NULL && iFd > 0 && close(iFd) != 0))
827 Res &= _error->Errno("close",_("Problem closing the file"));
828 iFd = -1;
829 gz = NULL;
830
831 if ((Flags & Fail) == Fail && (Flags & DelOnFail) == DelOnFail &&
832 FileName.empty() == false)
833 if (unlink(FileName.c_str()) != 0)
834 Res &= _error->WarningE("unlnk",_("Problem unlinking the file"));
835 return Res;
836}
837 /*}}}*/
838// FileFd::Sync - Sync the file /*{{{*/
839// ---------------------------------------------------------------------
840/* */
841bool FileFd::Sync()
842{
843#ifdef _POSIX_SYNCHRONIZED_IO
844 if (fsync(iFd) != 0)
845 return _error->Errno("sync",_("Problem syncing the file"));
846#endif
847 return true;
848}
849 /*}}}*/