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