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