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