]> git.saurik.com Git - apt.git/blob - apt-pkg/contrib/fileutl.cc
if the package has no installed & candidate but is virtual see if only
[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/strutl.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
45 using namespace std;
46
47 // RunScripts - Run a set of scripts from a configuration subtree /*{{{*/
48 // ---------------------------------------------------------------------
49 /* */
50 bool 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 */
112 bool 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. */
142 int 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 /* */
193 bool 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 // DirectoryExists - Check if a directory exists and is really one /*{{{*/
202 // ---------------------------------------------------------------------
203 /* */
204 bool DirectoryExists(string const &Path)
205 {
206 struct stat Buf;
207 if (stat(Path.c_str(),&Buf) != 0)
208 return false;
209 return ((Buf.st_mode & S_IFDIR) != 0);
210 }
211 /*}}}*/
212 // CreateDirectory - poor man's mkdir -p guarded by a parent directory /*{{{*/
213 // ---------------------------------------------------------------------
214 /* This method will create all directories needed for path in good old
215 mkdir -p style but refuses to do this if Parent is not a prefix of
216 this Path. Example: /var/cache/ and /var/cache/apt/archives are given,
217 so it will create apt/archives if /var/cache exists - on the other
218 hand if the parent is /var/lib the creation will fail as this path
219 is not a parent of the path to be generated. */
220 bool CreateDirectory(string const &Parent, string const &Path)
221 {
222 if (Parent.empty() == true || Path.empty() == true)
223 return false;
224
225 if (DirectoryExists(Path) == true)
226 return true;
227
228 if (DirectoryExists(Parent) == false)
229 return false;
230
231 // we are not going to create directories "into the blue"
232 if (Path.find(Parent, 0) != 0)
233 return false;
234
235 vector<string> const dirs = VectorizeString(Path.substr(Parent.size()), '/');
236 string progress = Parent;
237 for (vector<string>::const_iterator d = dirs.begin(); d != dirs.end(); ++d)
238 {
239 if (d->empty() == true)
240 continue;
241
242 progress.append("/").append(*d);
243 if (DirectoryExists(progress) == true)
244 continue;
245
246 if (mkdir(progress.c_str(), 0755) != 0)
247 return false;
248 }
249 return true;
250 }
251 /*}}}*/
252 // GetListOfFilesInDir - returns a vector of files in the given dir /*{{{*/
253 // ---------------------------------------------------------------------
254 /* If an extension is given only files with this extension are included
255 in the returned vector, otherwise every "normal" file is included. */
256 std::vector<string> GetListOfFilesInDir(string const &Dir, string const &Ext,
257 bool const &SortList, bool const &AllowNoExt)
258 {
259 std::vector<string> ext;
260 ext.reserve(2);
261 if (Ext.empty() == false)
262 ext.push_back(Ext);
263 if (AllowNoExt == true && ext.empty() == false)
264 ext.push_back("");
265 return GetListOfFilesInDir(Dir, ext, SortList);
266 }
267 std::vector<string> GetListOfFilesInDir(string const &Dir, std::vector<string> const &Ext,
268 bool const &SortList)
269 {
270 // Attention debuggers: need to be set with the environment config file!
271 bool const Debug = _config->FindB("Debug::GetListOfFilesInDir", false);
272 if (Debug == true)
273 {
274 std::clog << "Accept in " << Dir << " only files with the following " << Ext.size() << " extensions:" << std::endl;
275 if (Ext.empty() == true)
276 std::clog << "\tNO extension" << std::endl;
277 else
278 for (std::vector<string>::const_iterator e = Ext.begin();
279 e != Ext.end(); ++e)
280 std::clog << '\t' << (e->empty() == true ? "NO" : *e) << " extension" << std::endl;
281 }
282
283 std::vector<string> List;
284 Configuration::MatchAgainstConfig SilentIgnore("Dir::Ignore-Files-Silently");
285 DIR *D = opendir(Dir.c_str());
286 if (D == 0)
287 {
288 _error->Errno("opendir",_("Unable to read %s"),Dir.c_str());
289 return List;
290 }
291
292 for (struct dirent *Ent = readdir(D); Ent != 0; Ent = readdir(D))
293 {
294 // skip "hidden" files
295 if (Ent->d_name[0] == '.')
296 continue;
297
298 // check for accepted extension:
299 // no extension given -> periods are bad as hell!
300 // extensions given -> "" extension allows no extension
301 if (Ext.empty() == false)
302 {
303 string d_ext = flExtension(Ent->d_name);
304 if (d_ext == Ent->d_name) // no extension
305 {
306 if (std::find(Ext.begin(), Ext.end(), "") == Ext.end())
307 {
308 if (Debug == true)
309 std::clog << "Bad file: " << Ent->d_name << " → no extension" << std::endl;
310 _error->Notice("Ignoring file '%s' in directory '%s' as it has no filename extension", Ent->d_name, Dir.c_str());
311 continue;
312 }
313 }
314 else if (std::find(Ext.begin(), Ext.end(), d_ext) == Ext.end())
315 {
316 if (Debug == true)
317 std::clog << "Bad file: " << Ent->d_name << " → bad extension »" << flExtension(Ent->d_name) << "«" << std::endl;
318 if (SilentIgnore.Match(Ent->d_name) == false)
319 _error->Notice("Ignoring file '%s' in directory '%s' as it has an invalid filename extension", Ent->d_name, Dir.c_str());
320 continue;
321 }
322 }
323
324 // Skip bad filenames ala run-parts
325 const char *C = Ent->d_name;
326 for (; *C != 0; ++C)
327 if (isalpha(*C) == 0 && isdigit(*C) == 0
328 && *C != '_' && *C != '-') {
329 // no required extension -> dot is a bad character
330 if (*C == '.' && Ext.empty() == false)
331 continue;
332 break;
333 }
334
335 // we don't reach the end of the name -> bad character included
336 if (*C != 0)
337 {
338 if (Debug == true)
339 std::clog << "Bad file: " << Ent->d_name << " → bad character »"
340 << *C << "« in filename (period allowed: " << (Ext.empty() ? "no" : "yes") << ")" << std::endl;
341 continue;
342 }
343
344 // skip filenames which end with a period. These are never valid
345 if (*(C - 1) == '.')
346 {
347 if (Debug == true)
348 std::clog << "Bad file: " << Ent->d_name << " → Period as last character" << std::endl;
349 continue;
350 }
351
352 // Make sure it is a file and not something else
353 string const File = flCombine(Dir,Ent->d_name);
354 struct stat St;
355 if (stat(File.c_str(),&St) != 0 || S_ISREG(St.st_mode) == 0)
356 {
357 if (Debug == true)
358 std::clog << "Bad file: " << Ent->d_name << " → stat says not a good file" << std::endl;
359 continue;
360 }
361
362 if (Debug == true)
363 std::clog << "Accept file: " << Ent->d_name << " in " << Dir << std::endl;
364 List.push_back(File);
365 }
366 closedir(D);
367
368 if (SortList == true)
369 std::sort(List.begin(),List.end());
370 return List;
371 }
372 /*}}}*/
373 // SafeGetCWD - This is a safer getcwd that returns a dynamic string /*{{{*/
374 // ---------------------------------------------------------------------
375 /* We return / on failure. */
376 string SafeGetCWD()
377 {
378 // Stash the current dir.
379 char S[300];
380 S[0] = 0;
381 if (getcwd(S,sizeof(S)-2) == 0)
382 return "/";
383 unsigned int Len = strlen(S);
384 S[Len] = '/';
385 S[Len+1] = 0;
386 return S;
387 }
388 /*}}}*/
389 // flNotDir - Strip the directory from the filename /*{{{*/
390 // ---------------------------------------------------------------------
391 /* */
392 string flNotDir(string File)
393 {
394 string::size_type Res = File.rfind('/');
395 if (Res == string::npos)
396 return File;
397 Res++;
398 return string(File,Res,Res - File.length());
399 }
400 /*}}}*/
401 // flNotFile - Strip the file from the directory name /*{{{*/
402 // ---------------------------------------------------------------------
403 /* Result ends in a / */
404 string flNotFile(string File)
405 {
406 string::size_type Res = File.rfind('/');
407 if (Res == string::npos)
408 return "./";
409 Res++;
410 return string(File,0,Res);
411 }
412 /*}}}*/
413 // flExtension - Return the extension for the file /*{{{*/
414 // ---------------------------------------------------------------------
415 /* */
416 string flExtension(string File)
417 {
418 string::size_type Res = File.rfind('.');
419 if (Res == string::npos)
420 return File;
421 Res++;
422 return string(File,Res,Res - File.length());
423 }
424 /*}}}*/
425 // flNoLink - If file is a symlink then deref it /*{{{*/
426 // ---------------------------------------------------------------------
427 /* If the name is not a link then the returned path is the input. */
428 string flNoLink(string File)
429 {
430 struct stat St;
431 if (lstat(File.c_str(),&St) != 0 || S_ISLNK(St.st_mode) == 0)
432 return File;
433 if (stat(File.c_str(),&St) != 0)
434 return File;
435
436 /* Loop resolving the link. There is no need to limit the number of
437 loops because the stat call above ensures that the symlink is not
438 circular */
439 char Buffer[1024];
440 string NFile = File;
441 while (1)
442 {
443 // Read the link
444 int Res;
445 if ((Res = readlink(NFile.c_str(),Buffer,sizeof(Buffer))) <= 0 ||
446 (unsigned)Res >= sizeof(Buffer))
447 return File;
448
449 // Append or replace the previous path
450 Buffer[Res] = 0;
451 if (Buffer[0] == '/')
452 NFile = Buffer;
453 else
454 NFile = flNotFile(NFile) + Buffer;
455
456 // See if we are done
457 if (lstat(NFile.c_str(),&St) != 0)
458 return File;
459 if (S_ISLNK(St.st_mode) == 0)
460 return NFile;
461 }
462 }
463 /*}}}*/
464 // flCombine - Combine a file and a directory /*{{{*/
465 // ---------------------------------------------------------------------
466 /* If the file is an absolute path then it is just returned, otherwise
467 the directory is pre-pended to it. */
468 string flCombine(string Dir,string File)
469 {
470 if (File.empty() == true)
471 return string();
472
473 if (File[0] == '/' || Dir.empty() == true)
474 return File;
475 if (File.length() >= 2 && File[0] == '.' && File[1] == '/')
476 return File;
477 if (Dir[Dir.length()-1] == '/')
478 return Dir + File;
479 return Dir + '/' + File;
480 }
481 /*}}}*/
482 // SetCloseExec - Set the close on exec flag /*{{{*/
483 // ---------------------------------------------------------------------
484 /* */
485 void SetCloseExec(int Fd,bool Close)
486 {
487 if (fcntl(Fd,F_SETFD,(Close == false)?0:FD_CLOEXEC) != 0)
488 {
489 cerr << "FATAL -> Could not set close on exec " << strerror(errno) << endl;
490 exit(100);
491 }
492 }
493 /*}}}*/
494 // SetNonBlock - Set the nonblocking flag /*{{{*/
495 // ---------------------------------------------------------------------
496 /* */
497 void SetNonBlock(int Fd,bool Block)
498 {
499 int Flags = fcntl(Fd,F_GETFL) & (~O_NONBLOCK);
500 if (fcntl(Fd,F_SETFL,Flags | ((Block == false)?0:O_NONBLOCK)) != 0)
501 {
502 cerr << "FATAL -> Could not set non-blocking flag " << strerror(errno) << endl;
503 exit(100);
504 }
505 }
506 /*}}}*/
507 // WaitFd - Wait for a FD to become readable /*{{{*/
508 // ---------------------------------------------------------------------
509 /* This waits for a FD to become readable using select. It is useful for
510 applications making use of non-blocking sockets. The timeout is
511 in seconds. */
512 bool WaitFd(int Fd,bool write,unsigned long timeout)
513 {
514 fd_set Set;
515 struct timeval tv;
516 FD_ZERO(&Set);
517 FD_SET(Fd,&Set);
518 tv.tv_sec = timeout;
519 tv.tv_usec = 0;
520 if (write == true)
521 {
522 int Res;
523 do
524 {
525 Res = select(Fd+1,0,&Set,0,(timeout != 0?&tv:0));
526 }
527 while (Res < 0 && errno == EINTR);
528
529 if (Res <= 0)
530 return false;
531 }
532 else
533 {
534 int Res;
535 do
536 {
537 Res = select(Fd+1,&Set,0,0,(timeout != 0?&tv:0));
538 }
539 while (Res < 0 && errno == EINTR);
540
541 if (Res <= 0)
542 return false;
543 }
544
545 return true;
546 }
547 /*}}}*/
548 // ExecFork - Magical fork that sanitizes the context before execing /*{{{*/
549 // ---------------------------------------------------------------------
550 /* This is used if you want to cleanse the environment for the forked
551 child, it fixes up the important signals and nukes all of the fds,
552 otherwise acts like normal fork. */
553 pid_t ExecFork()
554 {
555 // Fork off the process
556 pid_t Process = fork();
557 if (Process < 0)
558 {
559 cerr << "FATAL -> Failed to fork." << endl;
560 exit(100);
561 }
562
563 // Spawn the subprocess
564 if (Process == 0)
565 {
566 // Setup the signals
567 signal(SIGPIPE,SIG_DFL);
568 signal(SIGQUIT,SIG_DFL);
569 signal(SIGINT,SIG_DFL);
570 signal(SIGWINCH,SIG_DFL);
571 signal(SIGCONT,SIG_DFL);
572 signal(SIGTSTP,SIG_DFL);
573
574 set<int> KeepFDs;
575 Configuration::Item const *Opts = _config->Tree("APT::Keep-Fds");
576 if (Opts != 0 && Opts->Child != 0)
577 {
578 Opts = Opts->Child;
579 for (; Opts != 0; Opts = Opts->Next)
580 {
581 if (Opts->Value.empty() == true)
582 continue;
583 int fd = atoi(Opts->Value.c_str());
584 KeepFDs.insert(fd);
585 }
586 }
587
588 // Close all of our FDs - just in case
589 for (int K = 3; K != 40; K++)
590 {
591 if(KeepFDs.find(K) == KeepFDs.end())
592 fcntl(K,F_SETFD,FD_CLOEXEC);
593 }
594 }
595
596 return Process;
597 }
598 /*}}}*/
599 // ExecWait - Fancy waitpid /*{{{*/
600 // ---------------------------------------------------------------------
601 /* Waits for the given sub process. If Reap is set then no errors are
602 generated. Otherwise a failed subprocess will generate a proper descriptive
603 message */
604 bool ExecWait(pid_t Pid,const char *Name,bool Reap)
605 {
606 if (Pid <= 1)
607 return true;
608
609 // Wait and collect the error code
610 int Status;
611 while (waitpid(Pid,&Status,0) != Pid)
612 {
613 if (errno == EINTR)
614 continue;
615
616 if (Reap == true)
617 return false;
618
619 return _error->Error(_("Waited for %s but it wasn't there"),Name);
620 }
621
622
623 // Check for an error code.
624 if (WIFEXITED(Status) == 0 || WEXITSTATUS(Status) != 0)
625 {
626 if (Reap == true)
627 return false;
628 if (WIFSIGNALED(Status) != 0)
629 {
630 if( WTERMSIG(Status) == SIGSEGV)
631 return _error->Error(_("Sub-process %s received a segmentation fault."),Name);
632 else
633 return _error->Error(_("Sub-process %s received signal %u."),Name, WTERMSIG(Status));
634 }
635
636 if (WIFEXITED(Status) != 0)
637 return _error->Error(_("Sub-process %s returned an error code (%u)"),Name,WEXITSTATUS(Status));
638
639 return _error->Error(_("Sub-process %s exited unexpectedly"),Name);
640 }
641
642 return true;
643 }
644 /*}}}*/
645
646 // FileFd::Open - Open a file /*{{{*/
647 // ---------------------------------------------------------------------
648 /* The most commonly used open mode combinations are given with Mode */
649 bool FileFd::Open(string FileName,OpenMode Mode, unsigned long Perms)
650 {
651 Close();
652 Flags = AutoClose;
653 switch (Mode)
654 {
655 case ReadOnly:
656 iFd = open(FileName.c_str(),O_RDONLY);
657 break;
658
659 case WriteEmpty:
660 {
661 struct stat Buf;
662 if (lstat(FileName.c_str(),&Buf) == 0 && S_ISLNK(Buf.st_mode))
663 unlink(FileName.c_str());
664 iFd = open(FileName.c_str(),O_RDWR | O_CREAT | O_TRUNC,Perms);
665 break;
666 }
667
668 case WriteExists:
669 iFd = open(FileName.c_str(),O_RDWR);
670 break;
671
672 case WriteAny:
673 iFd = open(FileName.c_str(),O_RDWR | O_CREAT,Perms);
674 break;
675
676 case WriteTemp:
677 unlink(FileName.c_str());
678 iFd = open(FileName.c_str(),O_RDWR | O_CREAT | O_EXCL,Perms);
679 break;
680 }
681
682 if (iFd < 0)
683 return _error->Errno("open",_("Could not open file %s"),FileName.c_str());
684
685 this->FileName = FileName;
686 SetCloseExec(iFd,true);
687 return true;
688 }
689 /*}}}*/
690 // FileFd::~File - Closes the file /*{{{*/
691 // ---------------------------------------------------------------------
692 /* If the proper modes are selected then we close the Fd and possibly
693 unlink the file on error. */
694 FileFd::~FileFd()
695 {
696 Close();
697 }
698 /*}}}*/
699 // FileFd::Read - Read a bit of the file /*{{{*/
700 // ---------------------------------------------------------------------
701 /* We are carefull to handle interruption by a signal while reading
702 gracefully. */
703 bool FileFd::Read(void *To,unsigned long Size,unsigned long *Actual)
704 {
705 int Res;
706 errno = 0;
707 if (Actual != 0)
708 *Actual = 0;
709
710 do
711 {
712 Res = read(iFd,To,Size);
713 if (Res < 0 && errno == EINTR)
714 continue;
715 if (Res < 0)
716 {
717 Flags |= Fail;
718 return _error->Errno("read",_("Read error"));
719 }
720
721 To = (char *)To + Res;
722 Size -= Res;
723 if (Actual != 0)
724 *Actual += Res;
725 }
726 while (Res > 0 && Size > 0);
727
728 if (Size == 0)
729 return true;
730
731 // Eof handling
732 if (Actual != 0)
733 {
734 Flags |= HitEof;
735 return true;
736 }
737
738 Flags |= Fail;
739 return _error->Error(_("read, still have %lu to read but none left"),Size);
740 }
741 /*}}}*/
742 // FileFd::Write - Write to the file /*{{{*/
743 // ---------------------------------------------------------------------
744 /* */
745 bool FileFd::Write(const void *From,unsigned long Size)
746 {
747 int Res;
748 errno = 0;
749 do
750 {
751 Res = write(iFd,From,Size);
752 if (Res < 0 && errno == EINTR)
753 continue;
754 if (Res < 0)
755 {
756 Flags |= Fail;
757 return _error->Errno("write",_("Write error"));
758 }
759
760 From = (char *)From + Res;
761 Size -= Res;
762 }
763 while (Res > 0 && Size > 0);
764
765 if (Size == 0)
766 return true;
767
768 Flags |= Fail;
769 return _error->Error(_("write, still have %lu to write but couldn't"),Size);
770 }
771 /*}}}*/
772 // FileFd::Seek - Seek in the file /*{{{*/
773 // ---------------------------------------------------------------------
774 /* */
775 bool FileFd::Seek(unsigned long To)
776 {
777 if (lseek(iFd,To,SEEK_SET) != (signed)To)
778 {
779 Flags |= Fail;
780 return _error->Error("Unable to seek to %lu",To);
781 }
782
783 return true;
784 }
785 /*}}}*/
786 // FileFd::Skip - Seek in the file /*{{{*/
787 // ---------------------------------------------------------------------
788 /* */
789 bool FileFd::Skip(unsigned long Over)
790 {
791 if (lseek(iFd,Over,SEEK_CUR) < 0)
792 {
793 Flags |= Fail;
794 return _error->Error("Unable to seek ahead %lu",Over);
795 }
796
797 return true;
798 }
799 /*}}}*/
800 // FileFd::Truncate - Truncate the file /*{{{*/
801 // ---------------------------------------------------------------------
802 /* */
803 bool FileFd::Truncate(unsigned long To)
804 {
805 if (ftruncate(iFd,To) != 0)
806 {
807 Flags |= Fail;
808 return _error->Error("Unable to truncate to %lu",To);
809 }
810
811 return true;
812 }
813 /*}}}*/
814 // FileFd::Tell - Current seek position /*{{{*/
815 // ---------------------------------------------------------------------
816 /* */
817 unsigned long FileFd::Tell()
818 {
819 off_t Res = lseek(iFd,0,SEEK_CUR);
820 if (Res == (off_t)-1)
821 _error->Errno("lseek","Failed to determine the current file position");
822 return Res;
823 }
824 /*}}}*/
825 // FileFd::Size - Return the size of the file /*{{{*/
826 // ---------------------------------------------------------------------
827 /* */
828 unsigned long FileFd::Size()
829 {
830 struct stat Buf;
831 if (fstat(iFd,&Buf) != 0)
832 return _error->Errno("fstat","Unable to determine the file size");
833 return Buf.st_size;
834 }
835 /*}}}*/
836 // FileFd::Close - Close the file if the close flag is set /*{{{*/
837 // ---------------------------------------------------------------------
838 /* */
839 bool FileFd::Close()
840 {
841 bool Res = true;
842 if ((Flags & AutoClose) == AutoClose)
843 if (iFd >= 0 && close(iFd) != 0)
844 Res &= _error->Errno("close",_("Problem closing the file"));
845 iFd = -1;
846
847 if ((Flags & Fail) == Fail && (Flags & DelOnFail) == DelOnFail &&
848 FileName.empty() == false)
849 if (unlink(FileName.c_str()) != 0)
850 Res &= _error->WarningE("unlnk",_("Problem unlinking the file"));
851 return Res;
852 }
853 /*}}}*/
854 // FileFd::Sync - Sync the file /*{{{*/
855 // ---------------------------------------------------------------------
856 /* */
857 bool FileFd::Sync()
858 {
859 #ifdef _POSIX_SYNCHRONIZED_IO
860 if (fsync(iFd) != 0)
861 return _error->Errno("sync",_("Problem syncing the file"));
862 #endif
863 return true;
864 }
865 /*}}}*/