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