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