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