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