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