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