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