]> git.saurik.com Git - apt.git/blame_incremental - apt-pkg/contrib/fileutl.cc
FileFd(): Drop file name extension check in ReadOnlyGzip mode
[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 break;
608
609 case ReadOnlyGzip:
610 iFd = open(FileName.c_str(),O_RDONLY);
611 if (iFd > 0) {
612 gz = gzdopen (iFd, "r");
613 if (gz == NULL) {
614 close (iFd);
615 iFd = -1;
616 }
617 }
618 break;
619
620 case WriteEmpty:
621 {
622 struct stat Buf;
623 if (lstat(FileName.c_str(),&Buf) == 0 && S_ISLNK(Buf.st_mode))
624 unlink(FileName.c_str());
625 iFd = open(FileName.c_str(),O_RDWR | O_CREAT | O_TRUNC,Perms);
626 break;
627 }
628
629 case WriteExists:
630 iFd = open(FileName.c_str(),O_RDWR);
631 break;
632
633 case WriteAny:
634 iFd = open(FileName.c_str(),O_RDWR | O_CREAT,Perms);
635 break;
636
637 case WriteTemp:
638 unlink(FileName.c_str());
639 iFd = open(FileName.c_str(),O_RDWR | O_CREAT | O_EXCL,Perms);
640 break;
641 }
642
643 if (iFd < 0)
644 return _error->Errno("open",_("Could not open file %s"),FileName.c_str());
645
646 this->FileName = FileName;
647 SetCloseExec(iFd,true);
648 return true;
649}
650 /*}}}*/
651// FileFd::~File - Closes the file /*{{{*/
652// ---------------------------------------------------------------------
653/* If the proper modes are selected then we close the Fd and possibly
654 unlink the file on error. */
655FileFd::~FileFd()
656{
657 Close();
658}
659 /*}}}*/
660// FileFd::Read - Read a bit of the file /*{{{*/
661// ---------------------------------------------------------------------
662/* We are carefull to handle interruption by a signal while reading
663 gracefully. */
664bool FileFd::Read(void *To,unsigned long Size,unsigned long *Actual)
665{
666 int Res;
667 errno = 0;
668 if (Actual != 0)
669 *Actual = 0;
670
671 do
672 {
673 if (gz != NULL)
674 Res = gzread(gz,To,Size);
675 else
676 Res = read(iFd,To,Size);
677 if (Res < 0 && errno == EINTR)
678 continue;
679 if (Res < 0)
680 {
681 Flags |= Fail;
682 return _error->Errno("read",_("Read error"));
683 }
684
685 To = (char *)To + Res;
686 Size -= Res;
687 if (Actual != 0)
688 *Actual += Res;
689 }
690 while (Res > 0 && Size > 0);
691
692 if (Size == 0)
693 return true;
694
695 // Eof handling
696 if (Actual != 0)
697 {
698 Flags |= HitEof;
699 return true;
700 }
701
702 Flags |= Fail;
703 return _error->Error(_("read, still have %lu to read but none left"),Size);
704}
705 /*}}}*/
706// FileFd::Write - Write to the file /*{{{*/
707// ---------------------------------------------------------------------
708/* */
709bool FileFd::Write(const void *From,unsigned long Size)
710{
711 int Res;
712 errno = 0;
713 do
714 {
715 if (gz != NULL)
716 Res = gzwrite(gz,From,Size);
717 else
718 Res = write(iFd,From,Size);
719 if (Res < 0 && errno == EINTR)
720 continue;
721 if (Res < 0)
722 {
723 Flags |= Fail;
724 return _error->Errno("write",_("Write error"));
725 }
726
727 From = (char *)From + Res;
728 Size -= Res;
729 }
730 while (Res > 0 && Size > 0);
731
732 if (Size == 0)
733 return true;
734
735 Flags |= Fail;
736 return _error->Error(_("write, still have %lu to write but couldn't"),Size);
737}
738 /*}}}*/
739// FileFd::Seek - Seek in the file /*{{{*/
740// ---------------------------------------------------------------------
741/* */
742bool FileFd::Seek(unsigned long To)
743{
744 int res;
745 if (gz)
746 res = gzseek(gz,To,SEEK_SET);
747 else
748 res = lseek(iFd,To,SEEK_SET);
749 if (res != (signed)To)
750 {
751 Flags |= Fail;
752 return _error->Error("Unable to seek to %lu",To);
753 }
754
755 return true;
756}
757 /*}}}*/
758// FileFd::Skip - Seek in the file /*{{{*/
759// ---------------------------------------------------------------------
760/* */
761bool FileFd::Skip(unsigned long Over)
762{
763 int res;
764 if (gz)
765 res = gzseek(gz,Over,SEEK_CUR);
766 else
767 res = lseek(iFd,Over,SEEK_CUR);
768 if (res < 0)
769 {
770 Flags |= Fail;
771 return _error->Error("Unable to seek ahead %lu",Over);
772 }
773
774 return true;
775}
776 /*}}}*/
777// FileFd::Truncate - Truncate the file /*{{{*/
778// ---------------------------------------------------------------------
779/* */
780bool FileFd::Truncate(unsigned long To)
781{
782 if (gz)
783 {
784 Flags |= Fail;
785 return _error->Error("Truncating gzipped files is not implemented (%s)", FileName.c_str());
786 }
787 if (ftruncate(iFd,To) != 0)
788 {
789 Flags |= Fail;
790 return _error->Error("Unable to truncate to %lu",To);
791 }
792
793 return true;
794}
795 /*}}}*/
796// FileFd::Tell - Current seek position /*{{{*/
797// ---------------------------------------------------------------------
798/* */
799unsigned long FileFd::Tell()
800{
801 off_t Res;
802 if (gz)
803 Res = gztell(gz);
804 else
805 Res = lseek(iFd,0,SEEK_CUR);
806 if (Res == (off_t)-1)
807 _error->Errno("lseek","Failed to determine the current file position");
808 return Res;
809}
810 /*}}}*/
811// FileFd::Size - Return the size of the file /*{{{*/
812// ---------------------------------------------------------------------
813/* */
814unsigned long FileFd::Size()
815{
816 //TODO: For gz, do we need the actual file size here or the uncompressed length?
817 struct stat Buf;
818 if (fstat(iFd,&Buf) != 0)
819 return _error->Errno("fstat","Unable to determine the file size");
820 return Buf.st_size;
821}
822 /*}}}*/
823// FileFd::Close - Close the file if the close flag is set /*{{{*/
824// ---------------------------------------------------------------------
825/* */
826bool FileFd::Close()
827{
828 bool Res = true;
829 if ((Flags & AutoClose) == AutoClose)
830 {
831 if (gz != NULL) {
832 int e = gzclose(gz);
833 // gzdopen() on empty files always fails with "buffer error" here, ignore that
834 if (e != 0 && e != Z_BUF_ERROR)
835 Res &= _error->Errno("close",_("Problem closing the gzip file"));
836 } else
837 if (iFd > 0 && close(iFd) != 0)
838 Res &= _error->Errno("close",_("Problem closing the file"));
839 }
840 iFd = -1;
841 gz = NULL;
842
843 if ((Flags & Fail) == Fail && (Flags & DelOnFail) == DelOnFail &&
844 FileName.empty() == false)
845 if (unlink(FileName.c_str()) != 0)
846 Res &= _error->WarningE("unlnk",_("Problem unlinking the file"));
847 return Res;
848}
849 /*}}}*/
850// FileFd::Sync - Sync the file /*{{{*/
851// ---------------------------------------------------------------------
852/* */
853bool FileFd::Sync()
854{
855#ifdef _POSIX_SYNCHRONIZED_IO
856 if (fsync(iFd) != 0)
857 return _error->Errno("sync",_("Problem syncing the file"));
858#endif
859 return true;
860}
861 /*}}}*/