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