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