]> git.saurik.com Git - apt.git/blame - apt-pkg/contrib/fileutl.cc
merged from debian-experimental2
[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
699b209e
DK
47// FIXME: Compressor Fds have some speed disadvantages and are a bit buggy currently,
48// so while the current implementation satisfies the testcases it is not a real option
49// to disable it for now
50#define APT_USE_ZLIB 1
aee1aac6 51#if APT_USE_ZLIB
032bd56f 52#include <zlib.h>
52b47296 53#else
561f860a 54#pragma message "Usage of zlib is DISABLED!"
699b209e 55#endif
032bd56f 56
2a79d5b5 57#ifdef WORDS_BIGENDIAN
2cae0ccb
DK
58#include <inttypes.h>
59#endif
ea542140
DK
60
61#include <apti18n.h>
578bfd0a
AL
62 /*}}}*/
63
4d055c05
AL
64using namespace std;
65
032bd56f
DK
66class FileFdPrivate {
67 public:
aee1aac6 68#if APT_USE_ZLIB
032bd56f 69 gzFile gz;
699b209e
DK
70#else
71 void* gz;
72#endif
561f860a 73 int compressed_fd;
699b209e
DK
74 pid_t compressor_pid;
75 bool pipe;
76 APT::Configuration::Compressor compressor;
52b47296 77 unsigned int openmode;
1abbc47c
DK
78 unsigned long long seekpos;
79 FileFdPrivate() : gz(NULL), compressed_fd(-1), compressor_pid(-1), pipe(false),
80 openmode(0), seekpos(0) {};
032bd56f
DK
81};
82
614adaa0
MV
83// RunScripts - Run a set of scripts from a configuration subtree /*{{{*/
84// ---------------------------------------------------------------------
85/* */
86bool RunScripts(const char *Cnf)
87{
88 Configuration::Item const *Opts = _config->Tree(Cnf);
89 if (Opts == 0 || Opts->Child == 0)
90 return true;
91 Opts = Opts->Child;
92
93 // Fork for running the system calls
94 pid_t Child = ExecFork();
95
96 // This is the child
97 if (Child == 0)
98 {
cfba4f69
MV
99 if (_config->FindDir("DPkg::Chroot-Directory","/") != "/")
100 {
101 std::cerr << "Chrooting into "
102 << _config->FindDir("DPkg::Chroot-Directory")
103 << std::endl;
104 if (chroot(_config->FindDir("DPkg::Chroot-Directory","/").c_str()) != 0)
105 _exit(100);
106 }
107
614adaa0
MV
108 if (chdir("/tmp/") != 0)
109 _exit(100);
110
111 unsigned int Count = 1;
112 for (; Opts != 0; Opts = Opts->Next, Count++)
113 {
114 if (Opts->Value.empty() == true)
115 continue;
116
117 if (system(Opts->Value.c_str()) != 0)
118 _exit(100+Count);
119 }
120 _exit(0);
121 }
122
123 // Wait for the child
124 int Status = 0;
125 while (waitpid(Child,&Status,0) != Child)
126 {
127 if (errno == EINTR)
128 continue;
129 return _error->Errno("waitpid","Couldn't wait for subprocess");
130 }
131
132 // Restore sig int/quit
133 signal(SIGQUIT,SIG_DFL);
134 signal(SIGINT,SIG_DFL);
135
136 // Check for an error code.
137 if (WIFEXITED(Status) == 0 || WEXITSTATUS(Status) != 0)
138 {
139 unsigned int Count = WEXITSTATUS(Status);
140 if (Count > 100)
141 {
142 Count -= 100;
143 for (; Opts != 0 && Count != 1; Opts = Opts->Next, Count--);
144 _error->Error("Problem executing scripts %s '%s'",Cnf,Opts->Value.c_str());
145 }
146
147 return _error->Error("Sub-process returned an error code");
148 }
149
150 return true;
151}
152 /*}}}*/
153
578bfd0a
AL
154// CopyFile - Buffered copy of a file /*{{{*/
155// ---------------------------------------------------------------------
156/* The caller is expected to set things so that failure causes erasure */
8b89e57f 157bool CopyFile(FileFd &From,FileFd &To)
578bfd0a
AL
158{
159 if (From.IsOpen() == false || To.IsOpen() == false)
160 return false;
161
162 // Buffered copy between fds
b2e465d6 163 SPtrArray<unsigned char> Buf = new unsigned char[64000];
650faab0 164 unsigned long long Size = From.Size();
b0db36b1 165 while (Size != 0)
578bfd0a 166 {
650faab0 167 unsigned long long ToRead = Size;
b0db36b1
AL
168 if (Size > 64000)
169 ToRead = 64000;
170
4a6d5862 171 if (From.Read(Buf,ToRead) == false ||
b0db36b1 172 To.Write(Buf,ToRead) == false)
578bfd0a 173 return false;
b0db36b1
AL
174
175 Size -= ToRead;
578bfd0a
AL
176 }
177
578bfd0a
AL
178 return true;
179}
180 /*}}}*/
181// GetLock - Gets a lock file /*{{{*/
182// ---------------------------------------------------------------------
183/* This will create an empty file of the given name and lock it. Once this
184 is done all other calls to GetLock in any other process will fail with
185 -1. The return result is the fd of the file, the call should call
186 close at some time. */
187int GetLock(string File,bool Errors)
188{
f659b39a
OS
189 // GetLock() is used in aptitude on directories with public-write access
190 // Use O_NOFOLLOW here to prevent symlink traversal attacks
191 int FD = open(File.c_str(),O_RDWR | O_CREAT | O_NOFOLLOW,0640);
578bfd0a
AL
192 if (FD < 0)
193 {
b2e465d6
AL
194 // Read only .. cant have locking problems there.
195 if (errno == EROFS)
196 {
197 _error->Warning(_("Not using locking for read only lock file %s"),File.c_str());
198 return dup(0); // Need something for the caller to close
199 }
200
578bfd0a 201 if (Errors == true)
b2e465d6
AL
202 _error->Errno("open",_("Could not open lock file %s"),File.c_str());
203
204 // Feh.. We do this to distinguish the lock vs open case..
205 errno = EPERM;
578bfd0a
AL
206 return -1;
207 }
b2e465d6
AL
208 SetCloseExec(FD,true);
209
578bfd0a
AL
210 // Aquire a write lock
211 struct flock fl;
c71bc556
AL
212 fl.l_type = F_WRLCK;
213 fl.l_whence = SEEK_SET;
214 fl.l_start = 0;
215 fl.l_len = 0;
578bfd0a
AL
216 if (fcntl(FD,F_SETLK,&fl) == -1)
217 {
d89df07a
AL
218 if (errno == ENOLCK)
219 {
b2e465d6
AL
220 _error->Warning(_("Not using locking for nfs mounted lock file %s"),File.c_str());
221 return dup(0); // Need something for the caller to close
d89df07a 222 }
578bfd0a 223 if (Errors == true)
b2e465d6
AL
224 _error->Errno("open",_("Could not get lock %s"),File.c_str());
225
226 int Tmp = errno;
578bfd0a 227 close(FD);
b2e465d6 228 errno = Tmp;
578bfd0a
AL
229 return -1;
230 }
231
232 return FD;
233}
234 /*}}}*/
235// FileExists - Check if a file exists /*{{{*/
236// ---------------------------------------------------------------------
36f1098a 237/* Beware: Directories are also files! */
578bfd0a
AL
238bool FileExists(string File)
239{
240 struct stat Buf;
241 if (stat(File.c_str(),&Buf) != 0)
242 return false;
243 return true;
244}
245 /*}}}*/
36f1098a
DK
246// RealFileExists - Check if a file exists and if it is really a file /*{{{*/
247// ---------------------------------------------------------------------
248/* */
249bool RealFileExists(string File)
250{
251 struct stat Buf;
252 if (stat(File.c_str(),&Buf) != 0)
253 return false;
254 return ((Buf.st_mode & S_IFREG) != 0);
255}
256 /*}}}*/
1cd1c398
DK
257// DirectoryExists - Check if a directory exists and is really one /*{{{*/
258// ---------------------------------------------------------------------
259/* */
260bool DirectoryExists(string const &Path)
261{
262 struct stat Buf;
263 if (stat(Path.c_str(),&Buf) != 0)
264 return false;
265 return ((Buf.st_mode & S_IFDIR) != 0);
266}
267 /*}}}*/
268// CreateDirectory - poor man's mkdir -p guarded by a parent directory /*{{{*/
269// ---------------------------------------------------------------------
270/* This method will create all directories needed for path in good old
271 mkdir -p style but refuses to do this if Parent is not a prefix of
272 this Path. Example: /var/cache/ and /var/cache/apt/archives are given,
273 so it will create apt/archives if /var/cache exists - on the other
274 hand if the parent is /var/lib the creation will fail as this path
275 is not a parent of the path to be generated. */
276bool CreateDirectory(string const &Parent, string const &Path)
277{
278 if (Parent.empty() == true || Path.empty() == true)
279 return false;
280
281 if (DirectoryExists(Path) == true)
282 return true;
283
284 if (DirectoryExists(Parent) == false)
285 return false;
286
287 // we are not going to create directories "into the blue"
288 if (Path.find(Parent, 0) != 0)
289 return false;
290
291 vector<string> const dirs = VectorizeString(Path.substr(Parent.size()), '/');
292 string progress = Parent;
293 for (vector<string>::const_iterator d = dirs.begin(); d != dirs.end(); ++d)
294 {
295 if (d->empty() == true)
296 continue;
297
298 progress.append("/").append(*d);
299 if (DirectoryExists(progress) == true)
300 continue;
301
302 if (mkdir(progress.c_str(), 0755) != 0)
303 return false;
304 }
305 return true;
306}
307 /*}}}*/
7753e468 308// CreateAPTDirectoryIfNeeded - ensure that the given directory exists /*{{{*/
b29c3712
DK
309// ---------------------------------------------------------------------
310/* a small wrapper around CreateDirectory to check if it exists and to
311 remove the trailing "/apt/" from the parent directory if needed */
7753e468 312bool CreateAPTDirectoryIfNeeded(string const &Parent, string const &Path)
b29c3712
DK
313{
314 if (DirectoryExists(Path) == true)
315 return true;
316
317 size_t const len = Parent.size();
318 if (len > 5 && Parent.find("/apt/", len - 6, 5) == len - 5)
319 {
320 if (CreateDirectory(Parent.substr(0,len-5), Path) == true)
321 return true;
322 }
323 else if (CreateDirectory(Parent, Path) == true)
324 return true;
325
326 return false;
327}
328 /*}}}*/
46e39c8e
MV
329// GetListOfFilesInDir - returns a vector of files in the given dir /*{{{*/
330// ---------------------------------------------------------------------
331/* If an extension is given only files with this extension are included
332 in the returned vector, otherwise every "normal" file is included. */
b39c1859
MV
333std::vector<string> GetListOfFilesInDir(string const &Dir, string const &Ext,
334 bool const &SortList, bool const &AllowNoExt)
335{
336 std::vector<string> ext;
337 ext.reserve(2);
338 if (Ext.empty() == false)
339 ext.push_back(Ext);
340 if (AllowNoExt == true && ext.empty() == false)
341 ext.push_back("");
342 return GetListOfFilesInDir(Dir, ext, SortList);
343}
344std::vector<string> GetListOfFilesInDir(string const &Dir, std::vector<string> const &Ext,
345 bool const &SortList)
346{
347 // Attention debuggers: need to be set with the environment config file!
348 bool const Debug = _config->FindB("Debug::GetListOfFilesInDir", false);
349 if (Debug == true)
350 {
351 std::clog << "Accept in " << Dir << " only files with the following " << Ext.size() << " extensions:" << std::endl;
352 if (Ext.empty() == true)
353 std::clog << "\tNO extension" << std::endl;
354 else
355 for (std::vector<string>::const_iterator e = Ext.begin();
356 e != Ext.end(); ++e)
357 std::clog << '\t' << (e->empty() == true ? "NO" : *e) << " extension" << std::endl;
358 }
359
46e39c8e 360 std::vector<string> List;
36f1098a
DK
361
362 if (DirectoryExists(Dir.c_str()) == false)
363 {
364 _error->Error(_("List of files can't be created as '%s' is not a directory"), Dir.c_str());
365 return List;
366 }
367
1408e219 368 Configuration::MatchAgainstConfig SilentIgnore("Dir::Ignore-Files-Silently");
46e39c8e
MV
369 DIR *D = opendir(Dir.c_str());
370 if (D == 0)
371 {
372 _error->Errno("opendir",_("Unable to read %s"),Dir.c_str());
373 return List;
374 }
375
376 for (struct dirent *Ent = readdir(D); Ent != 0; Ent = readdir(D))
377 {
b39c1859 378 // skip "hidden" files
46e39c8e
MV
379 if (Ent->d_name[0] == '.')
380 continue;
381
491058e3
DK
382 // Make sure it is a file and not something else
383 string const File = flCombine(Dir,Ent->d_name);
384#ifdef _DIRENT_HAVE_D_TYPE
385 if (Ent->d_type != DT_REG)
386#endif
387 {
388 if (RealFileExists(File.c_str()) == false)
389 {
84e254d6
DK
390 // do not show ignoration warnings for directories
391 if (
392#ifdef _DIRENT_HAVE_D_TYPE
393 Ent->d_type == DT_DIR ||
394#endif
395 DirectoryExists(File.c_str()) == true)
396 continue;
491058e3
DK
397 if (SilentIgnore.Match(Ent->d_name) == false)
398 _error->Notice(_("Ignoring '%s' in directory '%s' as it is not a regular file"), Ent->d_name, Dir.c_str());
399 continue;
400 }
401 }
402
b39c1859
MV
403 // check for accepted extension:
404 // no extension given -> periods are bad as hell!
405 // extensions given -> "" extension allows no extension
406 if (Ext.empty() == false)
407 {
408 string d_ext = flExtension(Ent->d_name);
409 if (d_ext == Ent->d_name) // no extension
410 {
411 if (std::find(Ext.begin(), Ext.end(), "") == Ext.end())
412 {
413 if (Debug == true)
414 std::clog << "Bad file: " << Ent->d_name << " → no extension" << std::endl;
5edc3966 415 if (SilentIgnore.Match(Ent->d_name) == false)
491058e3 416 _error->Notice(_("Ignoring file '%s' in directory '%s' as it has no filename extension"), Ent->d_name, Dir.c_str());
b39c1859
MV
417 continue;
418 }
419 }
420 else if (std::find(Ext.begin(), Ext.end(), d_ext) == Ext.end())
421 {
422 if (Debug == true)
423 std::clog << "Bad file: " << Ent->d_name << " → bad extension »" << flExtension(Ent->d_name) << "«" << std::endl;
1408e219 424 if (SilentIgnore.Match(Ent->d_name) == false)
491058e3 425 _error->Notice(_("Ignoring file '%s' in directory '%s' as it has an invalid filename extension"), Ent->d_name, Dir.c_str());
b39c1859
MV
426 continue;
427 }
428 }
46e39c8e 429
b39c1859 430 // Skip bad filenames ala run-parts
46e39c8e
MV
431 const char *C = Ent->d_name;
432 for (; *C != 0; ++C)
433 if (isalpha(*C) == 0 && isdigit(*C) == 0
b39c1859
MV
434 && *C != '_' && *C != '-') {
435 // no required extension -> dot is a bad character
436 if (*C == '.' && Ext.empty() == false)
437 continue;
46e39c8e 438 break;
b39c1859 439 }
46e39c8e 440
b39c1859 441 // we don't reach the end of the name -> bad character included
46e39c8e 442 if (*C != 0)
b39c1859
MV
443 {
444 if (Debug == true)
445 std::clog << "Bad file: " << Ent->d_name << " → bad character »"
446 << *C << "« in filename (period allowed: " << (Ext.empty() ? "no" : "yes") << ")" << std::endl;
447 continue;
448 }
449
fbb2c7e0
DK
450 // skip filenames which end with a period. These are never valid
451 if (*(C - 1) == '.')
452 {
453 if (Debug == true)
454 std::clog << "Bad file: " << Ent->d_name << " → Period as last character" << std::endl;
455 continue;
456 }
457
458 if (Debug == true)
459 std::clog << "Accept file: " << Ent->d_name << " in " << Dir << std::endl;
460 List.push_back(File);
461 }
462 closedir(D);
463
464 if (SortList == true)
465 std::sort(List.begin(),List.end());
466 return List;
467}
468std::vector<string> GetListOfFilesInDir(string const &Dir, bool SortList)
469{
470 bool const Debug = _config->FindB("Debug::GetListOfFilesInDir", false);
471 if (Debug == true)
472 std::clog << "Accept in " << Dir << " all regular files" << std::endl;
473
474 std::vector<string> List;
475
476 if (DirectoryExists(Dir.c_str()) == false)
477 {
478 _error->Error(_("List of files can't be created as '%s' is not a directory"), Dir.c_str());
479 return List;
480 }
481
482 DIR *D = opendir(Dir.c_str());
483 if (D == 0)
484 {
485 _error->Errno("opendir",_("Unable to read %s"),Dir.c_str());
486 return List;
487 }
488
489 for (struct dirent *Ent = readdir(D); Ent != 0; Ent = readdir(D))
490 {
491 // skip "hidden" files
492 if (Ent->d_name[0] == '.')
493 continue;
494
495 // Make sure it is a file and not something else
496 string const File = flCombine(Dir,Ent->d_name);
497#ifdef _DIRENT_HAVE_D_TYPE
498 if (Ent->d_type != DT_REG)
499#endif
500 {
501 if (RealFileExists(File.c_str()) == false)
502 {
503 if (Debug == true)
504 std::clog << "Bad file: " << Ent->d_name << " → it is not a real file" << std::endl;
505 continue;
506 }
507 }
508
509 // Skip bad filenames ala run-parts
510 const char *C = Ent->d_name;
511 for (; *C != 0; ++C)
512 if (isalpha(*C) == 0 && isdigit(*C) == 0
513 && *C != '_' && *C != '-' && *C != '.')
514 break;
515
516 // we don't reach the end of the name -> bad character included
517 if (*C != 0)
518 {
519 if (Debug == true)
520 std::clog << "Bad file: " << Ent->d_name << " → bad character »" << *C << "« in filename" << std::endl;
521 continue;
522 }
523
b39c1859
MV
524 // skip filenames which end with a period. These are never valid
525 if (*(C - 1) == '.')
526 {
527 if (Debug == true)
528 std::clog << "Bad file: " << Ent->d_name << " → Period as last character" << std::endl;
46e39c8e 529 continue;
b39c1859 530 }
46e39c8e 531
b39c1859
MV
532 if (Debug == true)
533 std::clog << "Accept file: " << Ent->d_name << " in " << Dir << std::endl;
46e39c8e
MV
534 List.push_back(File);
535 }
536 closedir(D);
537
538 if (SortList == true)
539 std::sort(List.begin(),List.end());
540 return List;
541}
542 /*}}}*/
578bfd0a
AL
543// SafeGetCWD - This is a safer getcwd that returns a dynamic string /*{{{*/
544// ---------------------------------------------------------------------
545/* We return / on failure. */
546string SafeGetCWD()
547{
548 // Stash the current dir.
549 char S[300];
550 S[0] = 0;
7f25bdff 551 if (getcwd(S,sizeof(S)-2) == 0)
578bfd0a 552 return "/";
7f25bdff
AL
553 unsigned int Len = strlen(S);
554 S[Len] = '/';
555 S[Len+1] = 0;
578bfd0a
AL
556 return S;
557}
558 /*}}}*/
2ec858bc
MV
559// GetModificationTime - Get the mtime of the given file or -1 on error /*{{{*/
560// ---------------------------------------------------------------------
561/* We return / on failure. */
562time_t GetModificationTime(string const &Path)
563{
564 struct stat St;
565 if (stat(Path.c_str(), &St) < 0)
566 return -1;
567 return St.st_mtime;
568}
569 /*}}}*/
8ce4327b
AL
570// flNotDir - Strip the directory from the filename /*{{{*/
571// ---------------------------------------------------------------------
572/* */
573string flNotDir(string File)
574{
575 string::size_type Res = File.rfind('/');
576 if (Res == string::npos)
577 return File;
578 Res++;
579 return string(File,Res,Res - File.length());
580}
581 /*}}}*/
d38b7b3d
AL
582// flNotFile - Strip the file from the directory name /*{{{*/
583// ---------------------------------------------------------------------
171c45bc 584/* Result ends in a / */
d38b7b3d
AL
585string flNotFile(string File)
586{
587 string::size_type Res = File.rfind('/');
588 if (Res == string::npos)
171c45bc 589 return "./";
d38b7b3d
AL
590 Res++;
591 return string(File,0,Res);
592}
593 /*}}}*/
b2e465d6
AL
594// flExtension - Return the extension for the file /*{{{*/
595// ---------------------------------------------------------------------
596/* */
597string flExtension(string File)
598{
599 string::size_type Res = File.rfind('.');
600 if (Res == string::npos)
601 return File;
602 Res++;
603 return string(File,Res,Res - File.length());
604}
605 /*}}}*/
421c8d10
AL
606// flNoLink - If file is a symlink then deref it /*{{{*/
607// ---------------------------------------------------------------------
608/* If the name is not a link then the returned path is the input. */
609string flNoLink(string File)
610{
611 struct stat St;
612 if (lstat(File.c_str(),&St) != 0 || S_ISLNK(St.st_mode) == 0)
613 return File;
614 if (stat(File.c_str(),&St) != 0)
615 return File;
616
617 /* Loop resolving the link. There is no need to limit the number of
618 loops because the stat call above ensures that the symlink is not
619 circular */
620 char Buffer[1024];
621 string NFile = File;
622 while (1)
623 {
624 // Read the link
625 int Res;
626 if ((Res = readlink(NFile.c_str(),Buffer,sizeof(Buffer))) <= 0 ||
627 (unsigned)Res >= sizeof(Buffer))
628 return File;
629
630 // Append or replace the previous path
631 Buffer[Res] = 0;
632 if (Buffer[0] == '/')
633 NFile = Buffer;
634 else
635 NFile = flNotFile(NFile) + Buffer;
636
637 // See if we are done
638 if (lstat(NFile.c_str(),&St) != 0)
639 return File;
640 if (S_ISLNK(St.st_mode) == 0)
641 return NFile;
642 }
643}
644 /*}}}*/
b2e465d6
AL
645// flCombine - Combine a file and a directory /*{{{*/
646// ---------------------------------------------------------------------
647/* If the file is an absolute path then it is just returned, otherwise
648 the directory is pre-pended to it. */
649string flCombine(string Dir,string File)
650{
651 if (File.empty() == true)
652 return string();
653
654 if (File[0] == '/' || Dir.empty() == true)
655 return File;
656 if (File.length() >= 2 && File[0] == '.' && File[1] == '/')
657 return File;
658 if (Dir[Dir.length()-1] == '/')
659 return Dir + File;
660 return Dir + '/' + File;
661}
662 /*}}}*/
3b5421b4
AL
663// SetCloseExec - Set the close on exec flag /*{{{*/
664// ---------------------------------------------------------------------
665/* */
666void SetCloseExec(int Fd,bool Close)
667{
668 if (fcntl(Fd,F_SETFD,(Close == false)?0:FD_CLOEXEC) != 0)
669 {
670 cerr << "FATAL -> Could not set close on exec " << strerror(errno) << endl;
671 exit(100);
672 }
673}
674 /*}}}*/
675// SetNonBlock - Set the nonblocking flag /*{{{*/
676// ---------------------------------------------------------------------
677/* */
678void SetNonBlock(int Fd,bool Block)
679{
0a8a80e5
AL
680 int Flags = fcntl(Fd,F_GETFL) & (~O_NONBLOCK);
681 if (fcntl(Fd,F_SETFL,Flags | ((Block == false)?0:O_NONBLOCK)) != 0)
3b5421b4
AL
682 {
683 cerr << "FATAL -> Could not set non-blocking flag " << strerror(errno) << endl;
684 exit(100);
685 }
686}
687 /*}}}*/
688// WaitFd - Wait for a FD to become readable /*{{{*/
689// ---------------------------------------------------------------------
b2e465d6 690/* This waits for a FD to become readable using select. It is useful for
6d5dd02a
AL
691 applications making use of non-blocking sockets. The timeout is
692 in seconds. */
1084d58a 693bool WaitFd(int Fd,bool write,unsigned long timeout)
3b5421b4
AL
694{
695 fd_set Set;
cc2313b7 696 struct timeval tv;
3b5421b4
AL
697 FD_ZERO(&Set);
698 FD_SET(Fd,&Set);
6d5dd02a
AL
699 tv.tv_sec = timeout;
700 tv.tv_usec = 0;
1084d58a 701 if (write == true)
b0db36b1
AL
702 {
703 int Res;
704 do
705 {
706 Res = select(Fd+1,0,&Set,0,(timeout != 0?&tv:0));
707 }
708 while (Res < 0 && errno == EINTR);
709
710 if (Res <= 0)
711 return false;
1084d58a
AL
712 }
713 else
714 {
b0db36b1
AL
715 int Res;
716 do
717 {
718 Res = select(Fd+1,&Set,0,0,(timeout != 0?&tv:0));
719 }
720 while (Res < 0 && errno == EINTR);
721
722 if (Res <= 0)
723 return false;
cc2313b7 724 }
1084d58a 725
3b5421b4
AL
726 return true;
727}
728 /*}}}*/
54676e1a
AL
729// ExecFork - Magical fork that sanitizes the context before execing /*{{{*/
730// ---------------------------------------------------------------------
731/* This is used if you want to cleanse the environment for the forked
732 child, it fixes up the important signals and nukes all of the fds,
733 otherwise acts like normal fork. */
75ef8f14 734pid_t ExecFork()
54676e1a
AL
735{
736 // Fork off the process
737 pid_t Process = fork();
738 if (Process < 0)
739 {
740 cerr << "FATAL -> Failed to fork." << endl;
741 exit(100);
742 }
743
744 // Spawn the subprocess
745 if (Process == 0)
746 {
747 // Setup the signals
748 signal(SIGPIPE,SIG_DFL);
749 signal(SIGQUIT,SIG_DFL);
750 signal(SIGINT,SIG_DFL);
751 signal(SIGWINCH,SIG_DFL);
752 signal(SIGCONT,SIG_DFL);
753 signal(SIGTSTP,SIG_DFL);
75ef8f14
MV
754
755 set<int> KeepFDs;
756 Configuration::Item const *Opts = _config->Tree("APT::Keep-Fds");
757 if (Opts != 0 && Opts->Child != 0)
758 {
759 Opts = Opts->Child;
760 for (; Opts != 0; Opts = Opts->Next)
761 {
762 if (Opts->Value.empty() == true)
763 continue;
764 int fd = atoi(Opts->Value.c_str());
765 KeepFDs.insert(fd);
766 }
767 }
768
54676e1a
AL
769 // Close all of our FDs - just in case
770 for (int K = 3; K != 40; K++)
75ef8f14
MV
771 {
772 if(KeepFDs.find(K) == KeepFDs.end())
007dc9e0 773 fcntl(K,F_SETFD,FD_CLOEXEC);
75ef8f14 774 }
54676e1a
AL
775 }
776
777 return Process;
778}
779 /*}}}*/
ddc1d8d0
AL
780// ExecWait - Fancy waitpid /*{{{*/
781// ---------------------------------------------------------------------
2c9a72d1 782/* Waits for the given sub process. If Reap is set then no errors are
ddc1d8d0
AL
783 generated. Otherwise a failed subprocess will generate a proper descriptive
784 message */
3826564e 785bool ExecWait(pid_t Pid,const char *Name,bool Reap)
ddc1d8d0
AL
786{
787 if (Pid <= 1)
788 return true;
789
790 // Wait and collect the error code
791 int Status;
792 while (waitpid(Pid,&Status,0) != Pid)
793 {
794 if (errno == EINTR)
795 continue;
796
797 if (Reap == true)
798 return false;
799
db0db9fe 800 return _error->Error(_("Waited for %s but it wasn't there"),Name);
ddc1d8d0
AL
801 }
802
803
804 // Check for an error code.
805 if (WIFEXITED(Status) == 0 || WEXITSTATUS(Status) != 0)
806 {
807 if (Reap == true)
808 return false;
ab7f4d7c 809 if (WIFSIGNALED(Status) != 0)
40e7fe0e 810 {
ab7f4d7c
MV
811 if( WTERMSIG(Status) == SIGSEGV)
812 return _error->Error(_("Sub-process %s received a segmentation fault."),Name);
813 else
814 return _error->Error(_("Sub-process %s received signal %u."),Name, WTERMSIG(Status));
40e7fe0e 815 }
ddc1d8d0
AL
816
817 if (WIFEXITED(Status) != 0)
b2e465d6 818 return _error->Error(_("Sub-process %s returned an error code (%u)"),Name,WEXITSTATUS(Status));
ddc1d8d0 819
b2e465d6 820 return _error->Error(_("Sub-process %s exited unexpectedly"),Name);
ddc1d8d0
AL
821 }
822
823 return true;
824}
825 /*}}}*/
578bfd0a 826
13d87e2e 827// FileFd::Open - Open a file /*{{{*/
578bfd0a
AL
828// ---------------------------------------------------------------------
829/* The most commonly used open mode combinations are given with Mode */
52b47296 830bool FileFd::Open(string FileName,unsigned int const Mode,CompressMode Compress, unsigned long const Perms)
578bfd0a 831{
257e8d66
DK
832 if (Mode == ReadOnlyGzip)
833 return Open(FileName, ReadOnly, Gzip, Perms);
257e8d66 834
468720c5
DK
835 if (Compress == Auto && (Mode & WriteOnly) == WriteOnly)
836 return _error->Error("Autodetection on %s only works in ReadOnly openmode!", FileName.c_str());
257e8d66 837
468720c5
DK
838 // FIXME: Denote inbuilt compressors somehow - as we don't need to have the binaries for them
839 std::vector<APT::Configuration::Compressor> const compressors = APT::Configuration::getCompressors();
840 std::vector<APT::Configuration::Compressor>::const_iterator compressor = compressors.begin();
841 if (Compress == Auto)
842 {
468720c5
DK
843 for (; compressor != compressors.end(); ++compressor)
844 {
845 std::string file = std::string(FileName).append(compressor->Extension);
846 if (FileExists(file) == false)
847 continue;
848 FileName = file;
468720c5
DK
849 break;
850 }
851 }
852 else if (Compress == Extension)
853 {
52b47296
DK
854 std::string::size_type const found = FileName.find_last_of('.');
855 std::string ext;
856 if (found != std::string::npos)
857 {
858 ext = FileName.substr(found);
859 if (ext == ".new" || ext == ".bak")
860 {
861 std::string::size_type const found2 = FileName.find_last_of('.', found - 1);
862 if (found2 != std::string::npos)
863 ext = FileName.substr(found2, found - found2);
864 else
865 ext.clear();
866 }
867 }
aee1aac6
DK
868 for (; compressor != compressors.end(); ++compressor)
869 if (ext == compressor->Extension)
870 break;
871 // no matching extension - assume uncompressed (imagine files like 'example.org_Packages')
872 if (compressor == compressors.end())
873 for (compressor = compressors.begin(); compressor != compressors.end(); ++compressor)
874 if (compressor->Name == ".")
468720c5 875 break;
468720c5 876 }
aee1aac6 877 else
468720c5
DK
878 {
879 std::string name;
880 switch (Compress)
881 {
aee1aac6 882 case None: name = "."; break;
468720c5
DK
883 case Gzip: name = "gzip"; break;
884 case Bzip2: name = "bzip2"; break;
885 case Lzma: name = "lzma"; break;
886 case Xz: name = "xz"; break;
aee1aac6
DK
887 case Auto:
888 case Extension:
52b47296
DK
889 // Unreachable
890 return _error->Error("Opening File %s in None, Auto or Extension should be already handled?!?", FileName.c_str());
468720c5
DK
891 }
892 for (; compressor != compressors.end(); ++compressor)
893 if (compressor->Name == name)
894 break;
aee1aac6 895 if (compressor == compressors.end())
468720c5
DK
896 return _error->Error("Can't find a configured compressor %s for file %s", name.c_str(), FileName.c_str());
897 }
898
aee1aac6
DK
899 if (compressor == compressors.end())
900 return _error->Error("Can't find a match for specified compressor mode for file %s", FileName.c_str());
901 return Open(FileName, Mode, *compressor, Perms);
902}
52b47296 903bool FileFd::Open(string FileName,unsigned int const Mode,APT::Configuration::Compressor const &compressor, unsigned long const Perms)
aee1aac6
DK
904{
905 Close();
906 d = new FileFdPrivate;
907 d->openmode = Mode;
908 Flags = AutoClose;
909
910 if ((Mode & WriteOnly) != WriteOnly && (Mode & (Atomic | Create | Empty | Exclusive)) != 0)
911 return _error->Error("ReadOnly mode for %s doesn't accept additional flags!", FileName.c_str());
912 if ((Mode & ReadWrite) == 0)
913 return _error->Error("No openmode provided in FileFd::Open for %s", FileName.c_str());
468720c5 914
257e8d66
DK
915 if ((Mode & Atomic) == Atomic)
916 {
917 Flags |= Replace;
918 char *name = strdup((FileName + ".XXXXXX").c_str());
919 TemporaryFileName = string(mktemp(name));
920 free(name);
921 }
922 else if ((Mode & (Exclusive | Create)) == (Exclusive | Create))
923 {
924 // for atomic, this will be done by rename in Close()
925 unlink(FileName.c_str());
926 }
927 if ((Mode & Empty) == Empty)
578bfd0a 928 {
257e8d66
DK
929 struct stat Buf;
930 if (lstat(FileName.c_str(),&Buf) == 0 && S_ISLNK(Buf.st_mode))
931 unlink(FileName.c_str());
932 }
c4fc2fd7 933
561f860a
DK
934 int fileflags = 0;
935 #define if_FLAGGED_SET(FLAG, MODE) if ((Mode & FLAG) == FLAG) fileflags |= MODE
936 if_FLAGGED_SET(ReadWrite, O_RDWR);
937 else if_FLAGGED_SET(ReadOnly, O_RDONLY);
938 else if_FLAGGED_SET(WriteOnly, O_WRONLY);
4a9db827 939
561f860a
DK
940 if_FLAGGED_SET(Create, O_CREAT);
941 if_FLAGGED_SET(Empty, O_TRUNC);
942 if_FLAGGED_SET(Exclusive, O_EXCL);
943 else if_FLAGGED_SET(Atomic, O_EXCL);
944 #undef if_FLAGGED_SET
52b47296 945
561f860a
DK
946 if (TemporaryFileName.empty() == false)
947 iFd = open(TemporaryFileName.c_str(), fileflags, Perms);
468720c5 948 else
561f860a 949 iFd = open(FileName.c_str(), fileflags, Perms);
468720c5 950
b711c01e 951 this->FileName = FileName;
561f860a
DK
952 if (iFd == -1 || OpenInternDescriptor(Mode, compressor) == false)
953 {
468720c5 954 if (iFd != -1)
fc81e8f2 955 {
561f860a
DK
956 close (iFd);
957 iFd = -1;
fc81e8f2 958 }
561f860a 959 return _error->Errno("open",_("Could not open file %s"), FileName.c_str());
257e8d66 960 }
578bfd0a 961
13d87e2e
AL
962 SetCloseExec(iFd,true);
963 return true;
578bfd0a 964}
257e8d66
DK
965 /*}}}*/
966// FileFd::OpenDescriptor - Open a filedescriptor /*{{{*/
967// ---------------------------------------------------------------------
968/* */
52b47296 969bool FileFd::OpenDescriptor(int Fd, unsigned int const Mode, CompressMode Compress, bool AutoClose)
aee1aac6
DK
970{
971 std::vector<APT::Configuration::Compressor> const compressors = APT::Configuration::getCompressors();
972 std::vector<APT::Configuration::Compressor>::const_iterator compressor = compressors.begin();
973 std::string name;
bce778a3
MV
974
975 // compat with the old API
976 if (Mode == ReadOnlyGzip && Compress == None)
977 Compress = Gzip;
978
aee1aac6
DK
979 switch (Compress)
980 {
981 case None: name = "."; break;
982 case Gzip: name = "gzip"; break;
983 case Bzip2: name = "bzip2"; break;
984 case Lzma: name = "lzma"; break;
985 case Xz: name = "xz"; break;
986 case Auto:
987 case Extension:
988 return _error->Error("Opening Fd %d in Auto or Extension compression mode is not supported", Fd);
989 }
990 for (; compressor != compressors.end(); ++compressor)
991 if (compressor->Name == name)
992 break;
993 if (compressor == compressors.end())
994 return _error->Error("Can't find a configured compressor %s for file %s", name.c_str(), FileName.c_str());
995
996 return OpenDescriptor(Fd, Mode, *compressor, AutoClose);
997}
52b47296 998bool FileFd::OpenDescriptor(int Fd, unsigned int const Mode, APT::Configuration::Compressor const &compressor, bool AutoClose)
144c0969
JAK
999{
1000 Close();
032bd56f 1001 d = new FileFdPrivate;
699b209e 1002 d->openmode = Mode;
144c0969
JAK
1003 Flags = (AutoClose) ? FileFd::AutoClose : 0;
1004 iFd = Fd;
b711c01e 1005 this->FileName = "";
aee1aac6 1006 if (OpenInternDescriptor(Mode, compressor) == false)
468720c5
DK
1007 {
1008 if (AutoClose)
1009 close (iFd);
1010 return _error->Errno("gzdopen",_("Could not open file descriptor %d"), Fd);
144c0969 1011 }
144c0969 1012 return true;
468720c5 1013}
52b47296 1014bool FileFd::OpenInternDescriptor(unsigned int const Mode, APT::Configuration::Compressor const &compressor)
468720c5 1015{
561f860a
DK
1016 d->compressor = compressor;
1017 if (compressor.Name == "." || compressor.Binary.empty() == true)
468720c5 1018 return true;
aee1aac6
DK
1019#if APT_USE_ZLIB
1020 else if (compressor.Name == "gzip")
468720c5
DK
1021 {
1022 if ((Mode & ReadWrite) == ReadWrite)
032bd56f 1023 d->gz = gzdopen(iFd, "r+");
468720c5 1024 else if ((Mode & WriteOnly) == WriteOnly)
032bd56f 1025 d->gz = gzdopen(iFd, "w");
468720c5 1026 else
032bd56f
DK
1027 d->gz = gzdopen (iFd, "r");
1028 if (d->gz == NULL)
468720c5 1029 return false;
032bd56f 1030 Flags |= Compressed;
561f860a 1031 return true;
468720c5 1032 }
699b209e 1033#endif
561f860a
DK
1034
1035 if ((Mode & ReadWrite) == ReadWrite)
1036 return _error->Error("ReadWrite mode is not supported for file %s", FileName.c_str());
1037
1038 bool const Comp = (Mode & WriteOnly) == WriteOnly;
1039 // Handle 'decompression' of empty files
1040 if (Comp == false)
1041 {
1042 struct stat Buf;
1043 fstat(iFd, &Buf);
1044 if (Buf.st_size == 0 && S_ISFIFO(Buf.st_mode) == false)
1045 return true;
1046
1047 // We don't need the file open - instead let the compressor open it
1048 // as he properly knows better how to efficiently read from 'his' file
1049 if (FileName.empty() == false)
1050 close(iFd);
1051 }
1052
1053 // Create a data pipe
1054 int Pipe[2] = {-1,-1};
1055 if (pipe(Pipe) != 0)
1056 return _error->Errno("pipe",_("Failed to create subprocess IPC"));
1057 for (int J = 0; J != 2; J++)
1058 SetCloseExec(Pipe[J],true);
1059
1060 d->compressed_fd = iFd;
1061 d->pipe = true;
1062
1063 if (Comp == true)
1064 iFd = Pipe[1];
1065 else
1066 iFd = Pipe[0];
1067
1068 // The child..
1069 d->compressor_pid = ExecFork();
1070 if (d->compressor_pid == 0)
1071 {
1072 if (Comp == true)
1073 {
1074 dup2(d->compressed_fd,STDOUT_FILENO);
1075 dup2(Pipe[0],STDIN_FILENO);
1076 }
1077 else
1078 {
1079 if (FileName.empty() == true)
1080 dup2(d->compressed_fd,STDIN_FILENO);
1081 dup2(Pipe[1],STDOUT_FILENO);
1082 }
1083
1084 SetCloseExec(STDOUT_FILENO,false);
1085 SetCloseExec(STDIN_FILENO,false);
1086
1087 std::vector<char const*> Args;
1088 Args.push_back(compressor.Binary.c_str());
1089 std::vector<std::string> const * const addArgs =
1090 (Comp == true) ? &(compressor.CompressArgs) : &(compressor.UncompressArgs);
1091 for (std::vector<std::string>::const_iterator a = addArgs->begin();
1092 a != addArgs->end(); ++a)
1093 Args.push_back(a->c_str());
1094 if (Comp == false && FileName.empty() == false)
1095 {
1096 Args.push_back("--stdout");
1097 if (TemporaryFileName.empty() == false)
1098 Args.push_back(TemporaryFileName.c_str());
1099 else
1100 Args.push_back(FileName.c_str());
1101 }
1102 Args.push_back(NULL);
1103
1104 execvp(Args[0],(char **)&Args[0]);
1105 cerr << _("Failed to exec compressor ") << Args[0] << endl;
1106 _exit(100);
1107 }
1108 if (Comp == true)
1109 close(Pipe[0]);
468720c5 1110 else
561f860a
DK
1111 close(Pipe[1]);
1112 if (Comp == true || FileName.empty() == true)
1113 close(d->compressed_fd);
1114
468720c5 1115 return true;
144c0969 1116}
578bfd0a 1117 /*}}}*/
8e06abb2 1118// FileFd::~File - Closes the file /*{{{*/
578bfd0a
AL
1119// ---------------------------------------------------------------------
1120/* If the proper modes are selected then we close the Fd and possibly
1121 unlink the file on error. */
8e06abb2 1122FileFd::~FileFd()
578bfd0a
AL
1123{
1124 Close();
1125}
1126 /*}}}*/
8e06abb2 1127// FileFd::Read - Read a bit of the file /*{{{*/
578bfd0a 1128// ---------------------------------------------------------------------
b0db36b1
AL
1129/* We are carefull to handle interruption by a signal while reading
1130 gracefully. */
650faab0 1131bool FileFd::Read(void *To,unsigned long long Size,unsigned long long *Actual)
578bfd0a 1132{
b0db36b1
AL
1133 int Res;
1134 errno = 0;
f604cf55
AL
1135 if (Actual != 0)
1136 *Actual = 0;
699b209e 1137 *((char *)To) = '\0';
b0db36b1 1138 do
578bfd0a 1139 {
aee1aac6 1140#if APT_USE_ZLIB
032bd56f
DK
1141 if (d->gz != NULL)
1142 Res = gzread(d->gz,To,Size);
a3a03f5d 1143 else
699b209e 1144#endif
a3a03f5d 1145 Res = read(iFd,To,Size);
b711c01e 1146
b0db36b1
AL
1147 if (Res < 0)
1148 {
b711c01e
DK
1149 if (errno == EINTR)
1150 continue;
b0db36b1 1151 Flags |= Fail;
b711c01e
DK
1152#if APT_USE_ZLIB
1153 if (d->gz != NULL)
1154 {
1155 int err;
1156 char const * const errmsg = gzerror(d->gz, &err);
1157 if (err != Z_ERRNO)
1158 return _error->Error("gzread: %s (%d: %s)", _("Read error"), err, errmsg);
1159 }
1160#endif
b2e465d6 1161 return _error->Errno("read",_("Read error"));
b0db36b1 1162 }
578bfd0a 1163
b0db36b1
AL
1164 To = (char *)To + Res;
1165 Size -= Res;
1abbc47c 1166 d->seekpos += Res;
f604cf55
AL
1167 if (Actual != 0)
1168 *Actual += Res;
b0db36b1
AL
1169 }
1170 while (Res > 0 && Size > 0);
1171
1172 if (Size == 0)
1173 return true;
1174
ddc1d8d0 1175 // Eof handling
f604cf55 1176 if (Actual != 0)
ddc1d8d0
AL
1177 {
1178 Flags |= HitEof;
1179 return true;
1180 }
1181
b0db36b1 1182 Flags |= Fail;
650faab0 1183 return _error->Error(_("read, still have %llu to read but none left"), Size);
578bfd0a
AL
1184}
1185 /*}}}*/
032bd56f
DK
1186// FileFd::ReadLine - Read a complete line from the file /*{{{*/
1187// ---------------------------------------------------------------------
1188/* Beware: This method can be quiet slow for big buffers on UNcompressed
1189 files because of the naive implementation! */
1190char* FileFd::ReadLine(char *To, unsigned long long const Size)
1191{
699b209e 1192 *To = '\0';
aee1aac6 1193#if APT_USE_ZLIB
032bd56f
DK
1194 if (d->gz != NULL)
1195 return gzgets(d->gz, To, Size);
699b209e 1196#endif
032bd56f
DK
1197
1198 unsigned long long read = 0;
40468850
DK
1199 while ((Size - 1) != read)
1200 {
1201 unsigned long long done = 0;
1202 if (Read(To + read, 1, &done) == false)
1203 return NULL;
1204 if (done == 0)
1205 break;
1206 if (To[read++] == '\n')
1207 break;
1208 }
1209 if (read == 0)
032bd56f 1210 return NULL;
40468850 1211 To[read] = '\0';
032bd56f
DK
1212 return To;
1213}
1214 /*}}}*/
8e06abb2 1215// FileFd::Write - Write to the file /*{{{*/
578bfd0a
AL
1216// ---------------------------------------------------------------------
1217/* */
650faab0 1218bool FileFd::Write(const void *From,unsigned long long Size)
578bfd0a 1219{
b0db36b1
AL
1220 int Res;
1221 errno = 0;
1222 do
578bfd0a 1223 {
aee1aac6 1224#if APT_USE_ZLIB
032bd56f
DK
1225 if (d->gz != NULL)
1226 Res = gzwrite(d->gz,From,Size);
a3a03f5d 1227 else
699b209e 1228#endif
a3a03f5d 1229 Res = write(iFd,From,Size);
b0db36b1
AL
1230 if (Res < 0 && errno == EINTR)
1231 continue;
1232 if (Res < 0)
1233 {
1234 Flags |= Fail;
b2e465d6 1235 return _error->Errno("write",_("Write error"));
b0db36b1
AL
1236 }
1237
1238 From = (char *)From + Res;
1239 Size -= Res;
1abbc47c 1240 d->seekpos += Res;
578bfd0a 1241 }
b0db36b1 1242 while (Res > 0 && Size > 0);
578bfd0a 1243
b0db36b1
AL
1244 if (Size == 0)
1245 return true;
1246
1247 Flags |= Fail;
650faab0 1248 return _error->Error(_("write, still have %llu to write but couldn't"), Size);
578bfd0a
AL
1249}
1250 /*}}}*/
8e06abb2 1251// FileFd::Seek - Seek in the file /*{{{*/
578bfd0a
AL
1252// ---------------------------------------------------------------------
1253/* */
650faab0 1254bool FileFd::Seek(unsigned long long To)
578bfd0a 1255{
699b209e
DK
1256 if (d->pipe == true)
1257 {
1abbc47c
DK
1258 // Our poor man seeking in pipes is costly, so try to avoid it
1259 unsigned long long seekpos = Tell();
1260 if (seekpos == To)
1261 return true;
1262 else if (seekpos < To)
1263 return Skip(To - seekpos);
1264
561f860a
DK
1265 if ((d->openmode & ReadOnly) != ReadOnly)
1266 return _error->Error("Reopen is only implemented for read-only files!");
699b209e 1267 close(iFd);
6fd947bd 1268 iFd = 0;
561f860a
DK
1269 if (TemporaryFileName.empty() == false)
1270 iFd = open(TemporaryFileName.c_str(), O_RDONLY);
1271 else if (FileName.empty() == false)
1272 iFd = open(FileName.c_str(), O_RDONLY);
1273 else
6fd947bd
DK
1274 {
1275 if (d->compressed_fd > 0)
1276 if (lseek(d->compressed_fd, 0, SEEK_SET) != 0)
1277 iFd = d->compressed_fd;
1278 if (iFd <= 0)
1279 return _error->Error("Reopen is not implemented for pipes opened with FileFd::OpenDescriptor()!");
1280 }
561f860a
DK
1281
1282 if (OpenInternDescriptor(d->openmode, d->compressor) == false)
1283 return _error->Error("Seek on file %s because it couldn't be reopened", FileName.c_str());
1284
1285 if (To != 0)
1286 return Skip(To);
1abbc47c
DK
1287
1288 d->seekpos = To;
561f860a 1289 return true;
699b209e 1290 }
a3a03f5d 1291 int res;
aee1aac6 1292#if APT_USE_ZLIB
032bd56f
DK
1293 if (d->gz)
1294 res = gzseek(d->gz,To,SEEK_SET);
a3a03f5d 1295 else
699b209e 1296#endif
a3a03f5d 1297 res = lseek(iFd,To,SEEK_SET);
1298 if (res != (signed)To)
578bfd0a
AL
1299 {
1300 Flags |= Fail;
650faab0 1301 return _error->Error("Unable to seek to %llu", To);
578bfd0a 1302 }
1abbc47c
DK
1303
1304 d->seekpos = To;
727f18af
AL
1305 return true;
1306}
1307 /*}}}*/
1308// FileFd::Skip - Seek in the file /*{{{*/
1309// ---------------------------------------------------------------------
1310/* */
650faab0 1311bool FileFd::Skip(unsigned long long Over)
727f18af 1312{
40468850
DK
1313 if (d->pipe == true)
1314 {
1315 d->seekpos += Over;
1316 char buffer[1024];
1317 while (Over != 0)
1318 {
1319 unsigned long long toread = std::min((unsigned long long) sizeof(buffer), Over);
1320 if (Read(buffer, toread) == false)
1321 return _error->Error("Unable to seek ahead %llu",Over);
1322 Over -= toread;
1323 }
1324 return true;
1325 }
1326
a3a03f5d 1327 int res;
aee1aac6 1328#if APT_USE_ZLIB
032bd56f
DK
1329 if (d->gz != NULL)
1330 res = gzseek(d->gz,Over,SEEK_CUR);
a3a03f5d 1331 else
699b209e 1332#endif
a3a03f5d 1333 res = lseek(iFd,Over,SEEK_CUR);
1334 if (res < 0)
727f18af
AL
1335 {
1336 Flags |= Fail;
650faab0 1337 return _error->Error("Unable to seek ahead %llu",Over);
727f18af 1338 }
1abbc47c
DK
1339 d->seekpos = res;
1340
6d5dd02a
AL
1341 return true;
1342}
1343 /*}}}*/
1344// FileFd::Truncate - Truncate the file /*{{{*/
1345// ---------------------------------------------------------------------
1346/* */
650faab0 1347bool FileFd::Truncate(unsigned long long To)
6d5dd02a 1348{
032bd56f 1349 if (d->gz != NULL)
a3a03f5d 1350 {
1351 Flags |= Fail;
1352 return _error->Error("Truncating gzipped files is not implemented (%s)", FileName.c_str());
1353 }
6d5dd02a
AL
1354 if (ftruncate(iFd,To) != 0)
1355 {
1356 Flags |= Fail;
650faab0 1357 return _error->Error("Unable to truncate to %llu",To);
6d5dd02a
AL
1358 }
1359
578bfd0a
AL
1360 return true;
1361}
1362 /*}}}*/
7f25bdff
AL
1363// FileFd::Tell - Current seek position /*{{{*/
1364// ---------------------------------------------------------------------
1365/* */
650faab0 1366unsigned long long FileFd::Tell()
7f25bdff 1367{
1abbc47c
DK
1368 // In theory, we could just return seekpos here always instead of
1369 // seeking around, but not all users of FileFd use always Seek() and co
1370 // so d->seekpos isn't always true and we can just use it as a hint if
1371 // we have nothing else, but not always as an authority…
1372 if (d->pipe == true)
1373 return d->seekpos;
1374
a3a03f5d 1375 off_t Res;
aee1aac6 1376#if APT_USE_ZLIB
032bd56f
DK
1377 if (d->gz != NULL)
1378 Res = gztell(d->gz);
a3a03f5d 1379 else
699b209e 1380#endif
a3a03f5d 1381 Res = lseek(iFd,0,SEEK_CUR);
7f25bdff
AL
1382 if (Res == (off_t)-1)
1383 _error->Errno("lseek","Failed to determine the current file position");
1abbc47c 1384 d->seekpos = Res;
7f25bdff
AL
1385 return Res;
1386}
1387 /*}}}*/
4260fd39 1388// FileFd::FileSize - Return the size of the file /*{{{*/
578bfd0a
AL
1389// ---------------------------------------------------------------------
1390/* */
650faab0 1391unsigned long long FileFd::FileSize()
578bfd0a
AL
1392{
1393 struct stat Buf;
699b209e 1394 if (d->pipe == false && fstat(iFd,&Buf) != 0)
44dc669e 1395 return _error->Errno("fstat","Unable to determine the file size");
699b209e
DK
1396
1397 // for compressor pipes st_size is undefined and at 'best' zero
1398 if (d->pipe == true || S_ISFIFO(Buf.st_mode))
1399 {
1400 // we set it here, too, as we get the info here for free
1401 // in theory the Open-methods should take care of it already
1402 d->pipe = true;
1403 if (stat(FileName.c_str(), &Buf) != 0)
1404 return _error->Errno("stat","Unable to determine the file size");
1405 }
1406
4260fd39
DK
1407 return Buf.st_size;
1408}
1409 /*}}}*/
1410// FileFd::Size - Return the size of the content in the file /*{{{*/
1411// ---------------------------------------------------------------------
1412/* */
650faab0 1413unsigned long long FileFd::Size()
4260fd39 1414{
650faab0 1415 unsigned long long size = FileSize();
44dc669e 1416
699b209e
DK
1417 // for compressor pipes st_size is undefined and at 'best' zero,
1418 // so we 'read' the content and 'seek' back - see there
1419 if (d->pipe == true)
1420 {
1abbc47c 1421 unsigned long long const oldSeek = Tell();
699b209e
DK
1422 char ignore[1000];
1423 unsigned long long read = 0;
1424 do {
1425 Read(ignore, sizeof(ignore), &read);
699b209e 1426 } while(read != 0);
1abbc47c
DK
1427 size = Tell();
1428 Seek(oldSeek);
699b209e 1429 }
aee1aac6 1430#if APT_USE_ZLIB
44dc669e 1431 // only check gzsize if we are actually a gzip file, just checking for
032bd56f 1432 // "gz" is not sufficient as uncompressed files could be opened with
44dc669e 1433 // gzopen in "direct" mode as well
699b209e 1434 else if (d->gz && !gzdirect(d->gz) && size > 0)
9c182afa 1435 {
65c72a4b 1436 off_t const oldPos = lseek(iFd,0,SEEK_CUR);
9c182afa
MP
1437 /* unfortunately zlib.h doesn't provide a gzsize(), so we have to do
1438 * this ourselves; the original (uncompressed) file size is the last 32
1439 * bits of the file */
650faab0 1440 // FIXME: Size for gz-files is limited by 32bit… no largefile support
9c182afa
MP
1441 if (lseek(iFd, -4, SEEK_END) < 0)
1442 return _error->Errno("lseek","Unable to seek to end of gzipped file");
f330c0f3 1443 size = 0L;
9c182afa
MP
1444 if (read(iFd, &size, 4) != 4)
1445 return _error->Errno("read","Unable to read original size of gzipped file");
f330c0f3
DK
1446
1447#ifdef WORDS_BIGENDIAN
2cae0ccb
DK
1448 uint32_t tmp_size = size;
1449 uint8_t const * const p = (uint8_t const * const) &tmp_size;
1450 tmp_size = (p[3] << 24) | (p[2] << 16) | (p[1] << 8) | p[0];
1451 size = tmp_size;
f330c0f3 1452#endif
9c182afa 1453
65c72a4b 1454 if (lseek(iFd, oldPos, SEEK_SET) < 0)
9c182afa 1455 return _error->Errno("lseek","Unable to seek in gzipped file");
65c72a4b 1456
9c182afa
MP
1457 return size;
1458 }
699b209e 1459#endif
9c182afa 1460
44dc669e 1461 return size;
578bfd0a
AL
1462}
1463 /*}}}*/
76a763e1
DK
1464// FileFd::ModificationTime - Return the time of last touch /*{{{*/
1465// ---------------------------------------------------------------------
1466/* */
1467time_t FileFd::ModificationTime()
1468{
1469 struct stat Buf;
699b209e 1470 if (d->pipe == false && fstat(iFd,&Buf) != 0)
76a763e1
DK
1471 {
1472 _error->Errno("fstat","Unable to determine the modification time of file %s", FileName.c_str());
1473 return 0;
1474 }
699b209e
DK
1475
1476 // for compressor pipes st_size is undefined and at 'best' zero
1477 if (d->pipe == true || S_ISFIFO(Buf.st_mode))
1478 {
1479 // we set it here, too, as we get the info here for free
1480 // in theory the Open-methods should take care of it already
1481 d->pipe = true;
1482 if (stat(FileName.c_str(), &Buf) != 0)
1483 {
1484 _error->Errno("fstat","Unable to determine the modification time of file %s", FileName.c_str());
1485 return 0;
1486 }
1487 }
1488
76a763e1
DK
1489 return Buf.st_mtime;
1490}
1491 /*}}}*/
8e06abb2 1492// FileFd::Close - Close the file if the close flag is set /*{{{*/
578bfd0a
AL
1493// ---------------------------------------------------------------------
1494/* */
8e06abb2 1495bool FileFd::Close()
578bfd0a 1496{
032bd56f
DK
1497 if (iFd == -1)
1498 return true;
1499
578bfd0a
AL
1500 bool Res = true;
1501 if ((Flags & AutoClose) == AutoClose)
d13c2d3f 1502 {
aee1aac6 1503#if APT_USE_ZLIB
032bd56f
DK
1504 if (d != NULL && d->gz != NULL) {
1505 int const e = gzclose(d->gz);
b711c01e 1506 // gzdclose() on empty files always fails with "buffer error" here, ignore that
d13c2d3f 1507 if (e != 0 && e != Z_BUF_ERROR)
3184b4cf 1508 Res &= _error->Errno("close",_("Problem closing the gzip file %s"), FileName.c_str());
d13c2d3f 1509 } else
699b209e 1510#endif
d13c2d3f 1511 if (iFd > 0 && close(iFd) != 0)
3184b4cf 1512 Res &= _error->Errno("close",_("Problem closing the file %s"), FileName.c_str());
d13c2d3f 1513 }
3010fb0e 1514
62d073d9 1515 if ((Flags & Replace) == Replace && iFd >= 0) {
3010fb0e 1516 if (rename(TemporaryFileName.c_str(), FileName.c_str()) != 0)
62d073d9
DK
1517 Res &= _error->Errno("rename",_("Problem renaming the file %s to %s"), TemporaryFileName.c_str(), FileName.c_str());
1518
fd3b761e 1519 FileName = TemporaryFileName; // for the unlink() below.
257e8d66 1520 TemporaryFileName.clear();
3010fb0e 1521 }
62d073d9
DK
1522
1523 iFd = -1;
1524
578bfd0a
AL
1525 if ((Flags & Fail) == Fail && (Flags & DelOnFail) == DelOnFail &&
1526 FileName.empty() == false)
1527 if (unlink(FileName.c_str()) != 0)
62d073d9 1528 Res &= _error->WarningE("unlnk",_("Problem unlinking the file %s"), FileName.c_str());
3010fb0e 1529
032bd56f
DK
1530 if (d != NULL)
1531 {
561f860a 1532 if (d->compressor_pid > 0)
52b47296 1533 ExecWait(d->compressor_pid, "FileFdCompressor", true);
032bd56f
DK
1534 delete d;
1535 d = NULL;
1536 }
3010fb0e 1537
578bfd0a
AL
1538 return Res;
1539}
1540 /*}}}*/
b2e465d6
AL
1541// FileFd::Sync - Sync the file /*{{{*/
1542// ---------------------------------------------------------------------
1543/* */
1544bool FileFd::Sync()
1545{
1546#ifdef _POSIX_SYNCHRONIZED_IO
1547 if (fsync(iFd) != 0)
1548 return _error->Errno("sync",_("Problem syncing the file"));
1549#endif
1550 return true;
1551}
1552 /*}}}*/
699b209e
DK
1553
1554gzFile FileFd::gzFd() { return (gzFile) d->gz; }