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