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