]> git.saurik.com Git - apt.git/blame - apt-pkg/contrib/fileutl.cc
* apt-pkg/cacheiterator.h:
[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
b39c1859
MV
339 // check for accepted extension:
340 // no extension given -> periods are bad as hell!
341 // extensions given -> "" extension allows no extension
342 if (Ext.empty() == false)
343 {
344 string d_ext = flExtension(Ent->d_name);
345 if (d_ext == Ent->d_name) // no extension
346 {
347 if (std::find(Ext.begin(), Ext.end(), "") == Ext.end())
348 {
349 if (Debug == true)
350 std::clog << "Bad file: " << Ent->d_name << " → no extension" << std::endl;
5edc3966
DK
351 if (SilentIgnore.Match(Ent->d_name) == false)
352 _error->Notice("Ignoring file '%s' in directory '%s' as it has no filename extension", Ent->d_name, Dir.c_str());
b39c1859
MV
353 continue;
354 }
355 }
356 else if (std::find(Ext.begin(), Ext.end(), d_ext) == Ext.end())
357 {
358 if (Debug == true)
359 std::clog << "Bad file: " << Ent->d_name << " → bad extension »" << flExtension(Ent->d_name) << "«" << std::endl;
1408e219
DK
360 if (SilentIgnore.Match(Ent->d_name) == false)
361 _error->Notice("Ignoring file '%s' in directory '%s' as it has an invalid filename extension", Ent->d_name, Dir.c_str());
b39c1859
MV
362 continue;
363 }
364 }
46e39c8e 365
b39c1859 366 // Skip bad filenames ala run-parts
46e39c8e
MV
367 const char *C = Ent->d_name;
368 for (; *C != 0; ++C)
369 if (isalpha(*C) == 0 && isdigit(*C) == 0
b39c1859
MV
370 && *C != '_' && *C != '-') {
371 // no required extension -> dot is a bad character
372 if (*C == '.' && Ext.empty() == false)
373 continue;
46e39c8e 374 break;
b39c1859 375 }
46e39c8e 376
b39c1859 377 // we don't reach the end of the name -> bad character included
46e39c8e 378 if (*C != 0)
b39c1859
MV
379 {
380 if (Debug == true)
381 std::clog << "Bad file: " << Ent->d_name << " → bad character »"
382 << *C << "« in filename (period allowed: " << (Ext.empty() ? "no" : "yes") << ")" << std::endl;
383 continue;
384 }
385
386 // skip filenames which end with a period. These are never valid
387 if (*(C - 1) == '.')
388 {
389 if (Debug == true)
390 std::clog << "Bad file: " << Ent->d_name << " → Period as last character" << std::endl;
46e39c8e 391 continue;
b39c1859 392 }
46e39c8e
MV
393
394 // Make sure it is a file and not something else
395 string const File = flCombine(Dir,Ent->d_name);
396 struct stat St;
397 if (stat(File.c_str(),&St) != 0 || S_ISREG(St.st_mode) == 0)
b39c1859
MV
398 {
399 if (Debug == true)
400 std::clog << "Bad file: " << Ent->d_name << " → stat says not a good file" << std::endl;
46e39c8e 401 continue;
b39c1859 402 }
46e39c8e 403
b39c1859
MV
404 if (Debug == true)
405 std::clog << "Accept file: " << Ent->d_name << " in " << Dir << std::endl;
46e39c8e
MV
406 List.push_back(File);
407 }
408 closedir(D);
409
410 if (SortList == true)
411 std::sort(List.begin(),List.end());
412 return List;
413}
414 /*}}}*/
578bfd0a
AL
415// SafeGetCWD - This is a safer getcwd that returns a dynamic string /*{{{*/
416// ---------------------------------------------------------------------
417/* We return / on failure. */
418string SafeGetCWD()
419{
420 // Stash the current dir.
421 char S[300];
422 S[0] = 0;
7f25bdff 423 if (getcwd(S,sizeof(S)-2) == 0)
578bfd0a 424 return "/";
7f25bdff
AL
425 unsigned int Len = strlen(S);
426 S[Len] = '/';
427 S[Len+1] = 0;
578bfd0a
AL
428 return S;
429}
430 /*}}}*/
8ce4327b
AL
431// flNotDir - Strip the directory from the filename /*{{{*/
432// ---------------------------------------------------------------------
433/* */
434string flNotDir(string File)
435{
436 string::size_type Res = File.rfind('/');
437 if (Res == string::npos)
438 return File;
439 Res++;
440 return string(File,Res,Res - File.length());
441}
442 /*}}}*/
d38b7b3d
AL
443// flNotFile - Strip the file from the directory name /*{{{*/
444// ---------------------------------------------------------------------
171c45bc 445/* Result ends in a / */
d38b7b3d
AL
446string flNotFile(string File)
447{
448 string::size_type Res = File.rfind('/');
449 if (Res == string::npos)
171c45bc 450 return "./";
d38b7b3d
AL
451 Res++;
452 return string(File,0,Res);
453}
454 /*}}}*/
b2e465d6
AL
455// flExtension - Return the extension for the file /*{{{*/
456// ---------------------------------------------------------------------
457/* */
458string flExtension(string File)
459{
460 string::size_type Res = File.rfind('.');
461 if (Res == string::npos)
462 return File;
463 Res++;
464 return string(File,Res,Res - File.length());
465}
466 /*}}}*/
421c8d10
AL
467// flNoLink - If file is a symlink then deref it /*{{{*/
468// ---------------------------------------------------------------------
469/* If the name is not a link then the returned path is the input. */
470string flNoLink(string File)
471{
472 struct stat St;
473 if (lstat(File.c_str(),&St) != 0 || S_ISLNK(St.st_mode) == 0)
474 return File;
475 if (stat(File.c_str(),&St) != 0)
476 return File;
477
478 /* Loop resolving the link. There is no need to limit the number of
479 loops because the stat call above ensures that the symlink is not
480 circular */
481 char Buffer[1024];
482 string NFile = File;
483 while (1)
484 {
485 // Read the link
486 int Res;
487 if ((Res = readlink(NFile.c_str(),Buffer,sizeof(Buffer))) <= 0 ||
488 (unsigned)Res >= sizeof(Buffer))
489 return File;
490
491 // Append or replace the previous path
492 Buffer[Res] = 0;
493 if (Buffer[0] == '/')
494 NFile = Buffer;
495 else
496 NFile = flNotFile(NFile) + Buffer;
497
498 // See if we are done
499 if (lstat(NFile.c_str(),&St) != 0)
500 return File;
501 if (S_ISLNK(St.st_mode) == 0)
502 return NFile;
503 }
504}
505 /*}}}*/
b2e465d6
AL
506// flCombine - Combine a file and a directory /*{{{*/
507// ---------------------------------------------------------------------
508/* If the file is an absolute path then it is just returned, otherwise
509 the directory is pre-pended to it. */
510string flCombine(string Dir,string File)
511{
512 if (File.empty() == true)
513 return string();
514
515 if (File[0] == '/' || Dir.empty() == true)
516 return File;
517 if (File.length() >= 2 && File[0] == '.' && File[1] == '/')
518 return File;
519 if (Dir[Dir.length()-1] == '/')
520 return Dir + File;
521 return Dir + '/' + File;
522}
523 /*}}}*/
3b5421b4
AL
524// SetCloseExec - Set the close on exec flag /*{{{*/
525// ---------------------------------------------------------------------
526/* */
527void SetCloseExec(int Fd,bool Close)
528{
529 if (fcntl(Fd,F_SETFD,(Close == false)?0:FD_CLOEXEC) != 0)
530 {
531 cerr << "FATAL -> Could not set close on exec " << strerror(errno) << endl;
532 exit(100);
533 }
534}
535 /*}}}*/
536// SetNonBlock - Set the nonblocking flag /*{{{*/
537// ---------------------------------------------------------------------
538/* */
539void SetNonBlock(int Fd,bool Block)
540{
0a8a80e5
AL
541 int Flags = fcntl(Fd,F_GETFL) & (~O_NONBLOCK);
542 if (fcntl(Fd,F_SETFL,Flags | ((Block == false)?0:O_NONBLOCK)) != 0)
3b5421b4
AL
543 {
544 cerr << "FATAL -> Could not set non-blocking flag " << strerror(errno) << endl;
545 exit(100);
546 }
547}
548 /*}}}*/
549// WaitFd - Wait for a FD to become readable /*{{{*/
550// ---------------------------------------------------------------------
b2e465d6 551/* This waits for a FD to become readable using select. It is useful for
6d5dd02a
AL
552 applications making use of non-blocking sockets. The timeout is
553 in seconds. */
1084d58a 554bool WaitFd(int Fd,bool write,unsigned long timeout)
3b5421b4
AL
555{
556 fd_set Set;
cc2313b7 557 struct timeval tv;
3b5421b4
AL
558 FD_ZERO(&Set);
559 FD_SET(Fd,&Set);
6d5dd02a
AL
560 tv.tv_sec = timeout;
561 tv.tv_usec = 0;
1084d58a 562 if (write == true)
b0db36b1
AL
563 {
564 int Res;
565 do
566 {
567 Res = select(Fd+1,0,&Set,0,(timeout != 0?&tv:0));
568 }
569 while (Res < 0 && errno == EINTR);
570
571 if (Res <= 0)
572 return false;
1084d58a
AL
573 }
574 else
575 {
b0db36b1
AL
576 int Res;
577 do
578 {
579 Res = select(Fd+1,&Set,0,0,(timeout != 0?&tv:0));
580 }
581 while (Res < 0 && errno == EINTR);
582
583 if (Res <= 0)
584 return false;
cc2313b7 585 }
1084d58a 586
3b5421b4
AL
587 return true;
588}
589 /*}}}*/
54676e1a
AL
590// ExecFork - Magical fork that sanitizes the context before execing /*{{{*/
591// ---------------------------------------------------------------------
592/* This is used if you want to cleanse the environment for the forked
593 child, it fixes up the important signals and nukes all of the fds,
594 otherwise acts like normal fork. */
75ef8f14 595pid_t ExecFork()
54676e1a
AL
596{
597 // Fork off the process
598 pid_t Process = fork();
599 if (Process < 0)
600 {
601 cerr << "FATAL -> Failed to fork." << endl;
602 exit(100);
603 }
604
605 // Spawn the subprocess
606 if (Process == 0)
607 {
608 // Setup the signals
609 signal(SIGPIPE,SIG_DFL);
610 signal(SIGQUIT,SIG_DFL);
611 signal(SIGINT,SIG_DFL);
612 signal(SIGWINCH,SIG_DFL);
613 signal(SIGCONT,SIG_DFL);
614 signal(SIGTSTP,SIG_DFL);
75ef8f14
MV
615
616 set<int> KeepFDs;
617 Configuration::Item const *Opts = _config->Tree("APT::Keep-Fds");
618 if (Opts != 0 && Opts->Child != 0)
619 {
620 Opts = Opts->Child;
621 for (; Opts != 0; Opts = Opts->Next)
622 {
623 if (Opts->Value.empty() == true)
624 continue;
625 int fd = atoi(Opts->Value.c_str());
626 KeepFDs.insert(fd);
627 }
628 }
629
54676e1a
AL
630 // Close all of our FDs - just in case
631 for (int K = 3; K != 40; K++)
75ef8f14
MV
632 {
633 if(KeepFDs.find(K) == KeepFDs.end())
007dc9e0 634 fcntl(K,F_SETFD,FD_CLOEXEC);
75ef8f14 635 }
54676e1a
AL
636 }
637
638 return Process;
639}
640 /*}}}*/
ddc1d8d0
AL
641// ExecWait - Fancy waitpid /*{{{*/
642// ---------------------------------------------------------------------
2c9a72d1 643/* Waits for the given sub process. If Reap is set then no errors are
ddc1d8d0
AL
644 generated. Otherwise a failed subprocess will generate a proper descriptive
645 message */
3826564e 646bool ExecWait(pid_t Pid,const char *Name,bool Reap)
ddc1d8d0
AL
647{
648 if (Pid <= 1)
649 return true;
650
651 // Wait and collect the error code
652 int Status;
653 while (waitpid(Pid,&Status,0) != Pid)
654 {
655 if (errno == EINTR)
656 continue;
657
658 if (Reap == true)
659 return false;
660
db0db9fe 661 return _error->Error(_("Waited for %s but it wasn't there"),Name);
ddc1d8d0
AL
662 }
663
664
665 // Check for an error code.
666 if (WIFEXITED(Status) == 0 || WEXITSTATUS(Status) != 0)
667 {
668 if (Reap == true)
669 return false;
ab7f4d7c 670 if (WIFSIGNALED(Status) != 0)
40e7fe0e 671 {
ab7f4d7c
MV
672 if( WTERMSIG(Status) == SIGSEGV)
673 return _error->Error(_("Sub-process %s received a segmentation fault."),Name);
674 else
675 return _error->Error(_("Sub-process %s received signal %u."),Name, WTERMSIG(Status));
40e7fe0e 676 }
ddc1d8d0
AL
677
678 if (WIFEXITED(Status) != 0)
b2e465d6 679 return _error->Error(_("Sub-process %s returned an error code (%u)"),Name,WEXITSTATUS(Status));
ddc1d8d0 680
b2e465d6 681 return _error->Error(_("Sub-process %s exited unexpectedly"),Name);
ddc1d8d0
AL
682 }
683
684 return true;
685}
686 /*}}}*/
578bfd0a 687
13d87e2e 688// FileFd::Open - Open a file /*{{{*/
578bfd0a
AL
689// ---------------------------------------------------------------------
690/* The most commonly used open mode combinations are given with Mode */
13d87e2e 691bool FileFd::Open(string FileName,OpenMode Mode, unsigned long Perms)
578bfd0a 692{
13d87e2e 693 Close();
1164783d 694 Flags = AutoClose;
578bfd0a
AL
695 switch (Mode)
696 {
697 case ReadOnly:
698 iFd = open(FileName.c_str(),O_RDONLY);
699 break;
c4fc2fd7 700
701 case ReadOnlyGzip:
702 iFd = open(FileName.c_str(),O_RDONLY);
d13c2d3f 703 if (iFd > 0) {
c4fc2fd7 704 gz = gzdopen (iFd, "r");
705 if (gz == NULL) {
706 close (iFd);
707 iFd = -1;
708 }
a3a03f5d 709 }
578bfd0a
AL
710 break;
711
4a9db827 712 case WriteAtomic:
50b513a1 713 {
3010fb0e
JAK
714 Flags |= Replace;
715 char *name = strdup((FileName + ".XXXXXX").c_str());
716 TemporaryFileName = string(mktemp(name));
717 iFd = open(TemporaryFileName.c_str(),O_RDWR | O_CREAT | O_EXCL,Perms);
718 free(name);
50b513a1
AL
719 break;
720 }
4a9db827 721
fc81e8f2
JAK
722 case WriteEmpty:
723 {
724 struct stat Buf;
725 if (lstat(FileName.c_str(),&Buf) == 0 && S_ISLNK(Buf.st_mode))
726 unlink(FileName.c_str());
727 iFd = open(FileName.c_str(),O_RDWR | O_CREAT | O_TRUNC,Perms);
728 break;
729 }
578bfd0a
AL
730
731 case WriteExists:
732 iFd = open(FileName.c_str(),O_RDWR);
733 break;
0a8e3465
AL
734
735 case WriteAny:
736 iFd = open(FileName.c_str(),O_RDWR | O_CREAT,Perms);
d38b7b3d 737 break;
f08fcf34
AL
738
739 case WriteTemp:
4decd43c
AL
740 unlink(FileName.c_str());
741 iFd = open(FileName.c_str(),O_RDWR | O_CREAT | O_EXCL,Perms);
f08fcf34 742 break;
578bfd0a
AL
743 }
744
745 if (iFd < 0)
b2e465d6 746 return _error->Errno("open",_("Could not open file %s"),FileName.c_str());
13d87e2e
AL
747
748 this->FileName = FileName;
749 SetCloseExec(iFd,true);
750 return true;
578bfd0a 751}
144c0969
JAK
752
753bool FileFd::OpenDescriptor(int Fd, OpenMode Mode, bool AutoClose)
754{
755 Close();
756 Flags = (AutoClose) ? FileFd::AutoClose : 0;
757 iFd = Fd;
758 if (Mode == ReadOnlyGzip) {
759 gz = gzdopen (iFd, "r");
760 if (gz == NULL) {
761 if (AutoClose)
762 close (iFd);
763 return _error->Errno("gzdopen",_("Could not open file descriptor %d"),
764 Fd);
765 }
766 }
767 this->FileName = "";
768 return true;
769}
578bfd0a 770 /*}}}*/
8e06abb2 771// FileFd::~File - Closes the file /*{{{*/
578bfd0a
AL
772// ---------------------------------------------------------------------
773/* If the proper modes are selected then we close the Fd and possibly
774 unlink the file on error. */
8e06abb2 775FileFd::~FileFd()
578bfd0a
AL
776{
777 Close();
778}
779 /*}}}*/
8e06abb2 780// FileFd::Read - Read a bit of the file /*{{{*/
578bfd0a 781// ---------------------------------------------------------------------
b0db36b1
AL
782/* We are carefull to handle interruption by a signal while reading
783 gracefully. */
f604cf55 784bool FileFd::Read(void *To,unsigned long Size,unsigned long *Actual)
578bfd0a 785{
b0db36b1
AL
786 int Res;
787 errno = 0;
f604cf55
AL
788 if (Actual != 0)
789 *Actual = 0;
790
b0db36b1 791 do
578bfd0a 792 {
a3a03f5d 793 if (gz != NULL)
794 Res = gzread(gz,To,Size);
795 else
796 Res = read(iFd,To,Size);
b0db36b1
AL
797 if (Res < 0 && errno == EINTR)
798 continue;
799 if (Res < 0)
800 {
801 Flags |= Fail;
b2e465d6 802 return _error->Errno("read",_("Read error"));
b0db36b1 803 }
578bfd0a 804
b0db36b1
AL
805 To = (char *)To + Res;
806 Size -= Res;
f604cf55
AL
807 if (Actual != 0)
808 *Actual += Res;
b0db36b1
AL
809 }
810 while (Res > 0 && Size > 0);
811
812 if (Size == 0)
813 return true;
814
ddc1d8d0 815 // Eof handling
f604cf55 816 if (Actual != 0)
ddc1d8d0
AL
817 {
818 Flags |= HitEof;
819 return true;
820 }
821
b0db36b1 822 Flags |= Fail;
b2e465d6 823 return _error->Error(_("read, still have %lu to read but none left"),Size);
578bfd0a
AL
824}
825 /*}}}*/
8e06abb2 826// FileFd::Write - Write to the file /*{{{*/
578bfd0a
AL
827// ---------------------------------------------------------------------
828/* */
a05599f1 829bool FileFd::Write(const void *From,unsigned long Size)
578bfd0a 830{
b0db36b1
AL
831 int Res;
832 errno = 0;
833 do
578bfd0a 834 {
a3a03f5d 835 if (gz != NULL)
836 Res = gzwrite(gz,From,Size);
837 else
838 Res = write(iFd,From,Size);
b0db36b1
AL
839 if (Res < 0 && errno == EINTR)
840 continue;
841 if (Res < 0)
842 {
843 Flags |= Fail;
b2e465d6 844 return _error->Errno("write",_("Write error"));
b0db36b1
AL
845 }
846
847 From = (char *)From + Res;
848 Size -= Res;
578bfd0a 849 }
b0db36b1 850 while (Res > 0 && Size > 0);
578bfd0a 851
b0db36b1
AL
852 if (Size == 0)
853 return true;
854
855 Flags |= Fail;
b2e465d6 856 return _error->Error(_("write, still have %lu to write but couldn't"),Size);
578bfd0a
AL
857}
858 /*}}}*/
8e06abb2 859// FileFd::Seek - Seek in the file /*{{{*/
578bfd0a
AL
860// ---------------------------------------------------------------------
861/* */
8e06abb2 862bool FileFd::Seek(unsigned long To)
578bfd0a 863{
a3a03f5d 864 int res;
865 if (gz)
866 res = gzseek(gz,To,SEEK_SET);
867 else
868 res = lseek(iFd,To,SEEK_SET);
869 if (res != (signed)To)
578bfd0a
AL
870 {
871 Flags |= Fail;
b2e465d6 872 return _error->Error("Unable to seek to %lu",To);
578bfd0a
AL
873 }
874
727f18af
AL
875 return true;
876}
877 /*}}}*/
878// FileFd::Skip - Seek in the file /*{{{*/
879// ---------------------------------------------------------------------
880/* */
881bool FileFd::Skip(unsigned long Over)
882{
a3a03f5d 883 int res;
884 if (gz)
885 res = gzseek(gz,Over,SEEK_CUR);
886 else
887 res = lseek(iFd,Over,SEEK_CUR);
888 if (res < 0)
727f18af
AL
889 {
890 Flags |= Fail;
b2e465d6 891 return _error->Error("Unable to seek ahead %lu",Over);
727f18af
AL
892 }
893
6d5dd02a
AL
894 return true;
895}
896 /*}}}*/
897// FileFd::Truncate - Truncate the file /*{{{*/
898// ---------------------------------------------------------------------
899/* */
900bool FileFd::Truncate(unsigned long To)
901{
a3a03f5d 902 if (gz)
903 {
904 Flags |= Fail;
905 return _error->Error("Truncating gzipped files is not implemented (%s)", FileName.c_str());
906 }
6d5dd02a
AL
907 if (ftruncate(iFd,To) != 0)
908 {
909 Flags |= Fail;
b2e465d6 910 return _error->Error("Unable to truncate to %lu",To);
6d5dd02a
AL
911 }
912
578bfd0a
AL
913 return true;
914}
915 /*}}}*/
7f25bdff
AL
916// FileFd::Tell - Current seek position /*{{{*/
917// ---------------------------------------------------------------------
918/* */
919unsigned long FileFd::Tell()
920{
a3a03f5d 921 off_t Res;
922 if (gz)
923 Res = gztell(gz);
924 else
925 Res = lseek(iFd,0,SEEK_CUR);
7f25bdff
AL
926 if (Res == (off_t)-1)
927 _error->Errno("lseek","Failed to determine the current file position");
928 return Res;
929}
930 /*}}}*/
4260fd39 931// FileFd::FileSize - Return the size of the file /*{{{*/
578bfd0a
AL
932// ---------------------------------------------------------------------
933/* */
4260fd39 934unsigned long FileFd::FileSize()
578bfd0a
AL
935{
936 struct stat Buf;
9c182afa 937
44dc669e
MV
938 if (fstat(iFd,&Buf) != 0)
939 return _error->Errno("fstat","Unable to determine the file size");
4260fd39
DK
940 return Buf.st_size;
941}
942 /*}}}*/
943// FileFd::Size - Return the size of the content in the file /*{{{*/
944// ---------------------------------------------------------------------
945/* */
946unsigned long FileFd::Size()
947{
948 unsigned long size = FileSize();
44dc669e
MV
949
950 // only check gzsize if we are actually a gzip file, just checking for
951 // "gz" is not sufficient as uncompressed files will be opened with
952 // gzopen in "direct" mode as well
953 if (gz && !gzdirect(gz) && size > 0)
9c182afa
MP
954 {
955 /* unfortunately zlib.h doesn't provide a gzsize(), so we have to do
956 * this ourselves; the original (uncompressed) file size is the last 32
957 * bits of the file */
4260fd39 958 off_t orig_pos = lseek(iFd, 0, SEEK_CUR);
9c182afa
MP
959 if (lseek(iFd, -4, SEEK_END) < 0)
960 return _error->Errno("lseek","Unable to seek to end of gzipped file");
961 if (read(iFd, &size, 4) != 4)
962 return _error->Errno("read","Unable to read original size of gzipped file");
963 size &= 0xFFFFFFFF;
964
965 if (lseek(iFd, orig_pos, SEEK_SET) < 0)
966 return _error->Errno("lseek","Unable to seek in gzipped file");
967 return size;
968 }
969
44dc669e 970 return size;
578bfd0a
AL
971}
972 /*}}}*/
8e06abb2 973// FileFd::Close - Close the file if the close flag is set /*{{{*/
578bfd0a
AL
974// ---------------------------------------------------------------------
975/* */
8e06abb2 976bool FileFd::Close()
578bfd0a
AL
977{
978 bool Res = true;
979 if ((Flags & AutoClose) == AutoClose)
d13c2d3f 980 {
981 if (gz != NULL) {
3184b4cf 982 int const e = gzclose(gz);
d13c2d3f 983 // gzdopen() on empty files always fails with "buffer error" here, ignore that
984 if (e != 0 && e != Z_BUF_ERROR)
3184b4cf 985 Res &= _error->Errno("close",_("Problem closing the gzip file %s"), FileName.c_str());
d13c2d3f 986 } else
987 if (iFd > 0 && close(iFd) != 0)
3184b4cf 988 Res &= _error->Errno("close",_("Problem closing the file %s"), FileName.c_str());
d13c2d3f 989 }
3010fb0e 990
62d073d9 991 if ((Flags & Replace) == Replace && iFd >= 0) {
3010fb0e 992 if (rename(TemporaryFileName.c_str(), FileName.c_str()) != 0)
62d073d9
DK
993 Res &= _error->Errno("rename",_("Problem renaming the file %s to %s"), TemporaryFileName.c_str(), FileName.c_str());
994
fd3b761e 995 FileName = TemporaryFileName; // for the unlink() below.
3010fb0e 996 }
62d073d9
DK
997
998 iFd = -1;
a3a03f5d 999 gz = NULL;
62d073d9 1000
578bfd0a
AL
1001 if ((Flags & Fail) == Fail && (Flags & DelOnFail) == DelOnFail &&
1002 FileName.empty() == false)
1003 if (unlink(FileName.c_str()) != 0)
62d073d9 1004 Res &= _error->WarningE("unlnk",_("Problem unlinking the file %s"), FileName.c_str());
3010fb0e
JAK
1005
1006
578bfd0a
AL
1007 return Res;
1008}
1009 /*}}}*/
b2e465d6
AL
1010// FileFd::Sync - Sync the file /*{{{*/
1011// ---------------------------------------------------------------------
1012/* */
1013bool FileFd::Sync()
1014{
1015#ifdef _POSIX_SYNCHRONIZED_IO
1016 if (fsync(iFd) != 0)
1017 return _error->Errno("sync",_("Problem syncing the file"));
1018#endif
1019 return true;
1020}
1021 /*}}}*/