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