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