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