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