]> git.saurik.com Git - apt.git/blame - apt-pkg/contrib/fileutl.cc
keep track of where we are in a filedescriptor so we can use it as Tell()
[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 {
390 if (SilentIgnore.Match(Ent->d_name) == false)
391 _error->Notice(_("Ignoring '%s' in directory '%s' as it is not a regular file"), Ent->d_name, Dir.c_str());
392 continue;
393 }
394 }
395
b39c1859
MV
396 // check for accepted extension:
397 // no extension given -> periods are bad as hell!
398 // extensions given -> "" extension allows no extension
399 if (Ext.empty() == false)
400 {
401 string d_ext = flExtension(Ent->d_name);
402 if (d_ext == Ent->d_name) // no extension
403 {
404 if (std::find(Ext.begin(), Ext.end(), "") == Ext.end())
405 {
406 if (Debug == true)
407 std::clog << "Bad file: " << Ent->d_name << " → no extension" << std::endl;
5edc3966 408 if (SilentIgnore.Match(Ent->d_name) == false)
491058e3 409 _error->Notice(_("Ignoring file '%s' in directory '%s' as it has no filename extension"), Ent->d_name, Dir.c_str());
b39c1859
MV
410 continue;
411 }
412 }
413 else if (std::find(Ext.begin(), Ext.end(), d_ext) == Ext.end())
414 {
415 if (Debug == true)
416 std::clog << "Bad file: " << Ent->d_name << " → bad extension »" << flExtension(Ent->d_name) << "«" << std::endl;
1408e219 417 if (SilentIgnore.Match(Ent->d_name) == false)
491058e3 418 _error->Notice(_("Ignoring file '%s' in directory '%s' as it has an invalid filename extension"), Ent->d_name, Dir.c_str());
b39c1859
MV
419 continue;
420 }
421 }
46e39c8e 422
b39c1859 423 // Skip bad filenames ala run-parts
46e39c8e
MV
424 const char *C = Ent->d_name;
425 for (; *C != 0; ++C)
426 if (isalpha(*C) == 0 && isdigit(*C) == 0
b39c1859
MV
427 && *C != '_' && *C != '-') {
428 // no required extension -> dot is a bad character
429 if (*C == '.' && Ext.empty() == false)
430 continue;
46e39c8e 431 break;
b39c1859 432 }
46e39c8e 433
b39c1859 434 // we don't reach the end of the name -> bad character included
46e39c8e 435 if (*C != 0)
b39c1859
MV
436 {
437 if (Debug == true)
438 std::clog << "Bad file: " << Ent->d_name << " → bad character »"
439 << *C << "« in filename (period allowed: " << (Ext.empty() ? "no" : "yes") << ")" << std::endl;
440 continue;
441 }
442
443 // skip filenames which end with a period. These are never valid
444 if (*(C - 1) == '.')
445 {
446 if (Debug == true)
447 std::clog << "Bad file: " << Ent->d_name << " → Period as last character" << std::endl;
46e39c8e 448 continue;
b39c1859 449 }
46e39c8e 450
b39c1859
MV
451 if (Debug == true)
452 std::clog << "Accept file: " << Ent->d_name << " in " << Dir << std::endl;
46e39c8e
MV
453 List.push_back(File);
454 }
455 closedir(D);
456
457 if (SortList == true)
458 std::sort(List.begin(),List.end());
459 return List;
460}
461 /*}}}*/
578bfd0a
AL
462// SafeGetCWD - This is a safer getcwd that returns a dynamic string /*{{{*/
463// ---------------------------------------------------------------------
464/* We return / on failure. */
465string SafeGetCWD()
466{
467 // Stash the current dir.
468 char S[300];
469 S[0] = 0;
7f25bdff 470 if (getcwd(S,sizeof(S)-2) == 0)
578bfd0a 471 return "/";
7f25bdff
AL
472 unsigned int Len = strlen(S);
473 S[Len] = '/';
474 S[Len+1] = 0;
578bfd0a
AL
475 return S;
476}
477 /*}}}*/
2ec858bc
MV
478// GetModificationTime - Get the mtime of the given file or -1 on error /*{{{*/
479// ---------------------------------------------------------------------
480/* We return / on failure. */
481time_t GetModificationTime(string const &Path)
482{
483 struct stat St;
484 if (stat(Path.c_str(), &St) < 0)
485 return -1;
486 return St.st_mtime;
487}
488 /*}}}*/
8ce4327b
AL
489// flNotDir - Strip the directory from the filename /*{{{*/
490// ---------------------------------------------------------------------
491/* */
492string flNotDir(string File)
493{
494 string::size_type Res = File.rfind('/');
495 if (Res == string::npos)
496 return File;
497 Res++;
498 return string(File,Res,Res - File.length());
499}
500 /*}}}*/
d38b7b3d
AL
501// flNotFile - Strip the file from the directory name /*{{{*/
502// ---------------------------------------------------------------------
171c45bc 503/* Result ends in a / */
d38b7b3d
AL
504string flNotFile(string File)
505{
506 string::size_type Res = File.rfind('/');
507 if (Res == string::npos)
171c45bc 508 return "./";
d38b7b3d
AL
509 Res++;
510 return string(File,0,Res);
511}
512 /*}}}*/
b2e465d6
AL
513// flExtension - Return the extension for the file /*{{{*/
514// ---------------------------------------------------------------------
515/* */
516string flExtension(string File)
517{
518 string::size_type Res = File.rfind('.');
519 if (Res == string::npos)
520 return File;
521 Res++;
522 return string(File,Res,Res - File.length());
523}
524 /*}}}*/
421c8d10
AL
525// flNoLink - If file is a symlink then deref it /*{{{*/
526// ---------------------------------------------------------------------
527/* If the name is not a link then the returned path is the input. */
528string flNoLink(string File)
529{
530 struct stat St;
531 if (lstat(File.c_str(),&St) != 0 || S_ISLNK(St.st_mode) == 0)
532 return File;
533 if (stat(File.c_str(),&St) != 0)
534 return File;
535
536 /* Loop resolving the link. There is no need to limit the number of
537 loops because the stat call above ensures that the symlink is not
538 circular */
539 char Buffer[1024];
540 string NFile = File;
541 while (1)
542 {
543 // Read the link
544 int Res;
545 if ((Res = readlink(NFile.c_str(),Buffer,sizeof(Buffer))) <= 0 ||
546 (unsigned)Res >= sizeof(Buffer))
547 return File;
548
549 // Append or replace the previous path
550 Buffer[Res] = 0;
551 if (Buffer[0] == '/')
552 NFile = Buffer;
553 else
554 NFile = flNotFile(NFile) + Buffer;
555
556 // See if we are done
557 if (lstat(NFile.c_str(),&St) != 0)
558 return File;
559 if (S_ISLNK(St.st_mode) == 0)
560 return NFile;
561 }
562}
563 /*}}}*/
b2e465d6
AL
564// flCombine - Combine a file and a directory /*{{{*/
565// ---------------------------------------------------------------------
566/* If the file is an absolute path then it is just returned, otherwise
567 the directory is pre-pended to it. */
568string flCombine(string Dir,string File)
569{
570 if (File.empty() == true)
571 return string();
572
573 if (File[0] == '/' || Dir.empty() == true)
574 return File;
575 if (File.length() >= 2 && File[0] == '.' && File[1] == '/')
576 return File;
577 if (Dir[Dir.length()-1] == '/')
578 return Dir + File;
579 return Dir + '/' + File;
580}
581 /*}}}*/
3b5421b4
AL
582// SetCloseExec - Set the close on exec flag /*{{{*/
583// ---------------------------------------------------------------------
584/* */
585void SetCloseExec(int Fd,bool Close)
586{
587 if (fcntl(Fd,F_SETFD,(Close == false)?0:FD_CLOEXEC) != 0)
588 {
589 cerr << "FATAL -> Could not set close on exec " << strerror(errno) << endl;
590 exit(100);
591 }
592}
593 /*}}}*/
594// SetNonBlock - Set the nonblocking flag /*{{{*/
595// ---------------------------------------------------------------------
596/* */
597void SetNonBlock(int Fd,bool Block)
598{
0a8a80e5
AL
599 int Flags = fcntl(Fd,F_GETFL) & (~O_NONBLOCK);
600 if (fcntl(Fd,F_SETFL,Flags | ((Block == false)?0:O_NONBLOCK)) != 0)
3b5421b4
AL
601 {
602 cerr << "FATAL -> Could not set non-blocking flag " << strerror(errno) << endl;
603 exit(100);
604 }
605}
606 /*}}}*/
607// WaitFd - Wait for a FD to become readable /*{{{*/
608// ---------------------------------------------------------------------
b2e465d6 609/* This waits for a FD to become readable using select. It is useful for
6d5dd02a
AL
610 applications making use of non-blocking sockets. The timeout is
611 in seconds. */
1084d58a 612bool WaitFd(int Fd,bool write,unsigned long timeout)
3b5421b4
AL
613{
614 fd_set Set;
cc2313b7 615 struct timeval tv;
3b5421b4
AL
616 FD_ZERO(&Set);
617 FD_SET(Fd,&Set);
6d5dd02a
AL
618 tv.tv_sec = timeout;
619 tv.tv_usec = 0;
1084d58a 620 if (write == true)
b0db36b1
AL
621 {
622 int Res;
623 do
624 {
625 Res = select(Fd+1,0,&Set,0,(timeout != 0?&tv:0));
626 }
627 while (Res < 0 && errno == EINTR);
628
629 if (Res <= 0)
630 return false;
1084d58a
AL
631 }
632 else
633 {
b0db36b1
AL
634 int Res;
635 do
636 {
637 Res = select(Fd+1,&Set,0,0,(timeout != 0?&tv:0));
638 }
639 while (Res < 0 && errno == EINTR);
640
641 if (Res <= 0)
642 return false;
cc2313b7 643 }
1084d58a 644
3b5421b4
AL
645 return true;
646}
647 /*}}}*/
54676e1a
AL
648// ExecFork - Magical fork that sanitizes the context before execing /*{{{*/
649// ---------------------------------------------------------------------
650/* This is used if you want to cleanse the environment for the forked
651 child, it fixes up the important signals and nukes all of the fds,
652 otherwise acts like normal fork. */
75ef8f14 653pid_t ExecFork()
54676e1a
AL
654{
655 // Fork off the process
656 pid_t Process = fork();
657 if (Process < 0)
658 {
659 cerr << "FATAL -> Failed to fork." << endl;
660 exit(100);
661 }
662
663 // Spawn the subprocess
664 if (Process == 0)
665 {
666 // Setup the signals
667 signal(SIGPIPE,SIG_DFL);
668 signal(SIGQUIT,SIG_DFL);
669 signal(SIGINT,SIG_DFL);
670 signal(SIGWINCH,SIG_DFL);
671 signal(SIGCONT,SIG_DFL);
672 signal(SIGTSTP,SIG_DFL);
75ef8f14
MV
673
674 set<int> KeepFDs;
675 Configuration::Item const *Opts = _config->Tree("APT::Keep-Fds");
676 if (Opts != 0 && Opts->Child != 0)
677 {
678 Opts = Opts->Child;
679 for (; Opts != 0; Opts = Opts->Next)
680 {
681 if (Opts->Value.empty() == true)
682 continue;
683 int fd = atoi(Opts->Value.c_str());
684 KeepFDs.insert(fd);
685 }
686 }
687
54676e1a
AL
688 // Close all of our FDs - just in case
689 for (int K = 3; K != 40; K++)
75ef8f14
MV
690 {
691 if(KeepFDs.find(K) == KeepFDs.end())
007dc9e0 692 fcntl(K,F_SETFD,FD_CLOEXEC);
75ef8f14 693 }
54676e1a
AL
694 }
695
696 return Process;
697}
698 /*}}}*/
ddc1d8d0
AL
699// ExecWait - Fancy waitpid /*{{{*/
700// ---------------------------------------------------------------------
2c9a72d1 701/* Waits for the given sub process. If Reap is set then no errors are
ddc1d8d0
AL
702 generated. Otherwise a failed subprocess will generate a proper descriptive
703 message */
3826564e 704bool ExecWait(pid_t Pid,const char *Name,bool Reap)
ddc1d8d0
AL
705{
706 if (Pid <= 1)
707 return true;
708
709 // Wait and collect the error code
710 int Status;
711 while (waitpid(Pid,&Status,0) != Pid)
712 {
713 if (errno == EINTR)
714 continue;
715
716 if (Reap == true)
717 return false;
718
db0db9fe 719 return _error->Error(_("Waited for %s but it wasn't there"),Name);
ddc1d8d0
AL
720 }
721
722
723 // Check for an error code.
724 if (WIFEXITED(Status) == 0 || WEXITSTATUS(Status) != 0)
725 {
726 if (Reap == true)
727 return false;
ab7f4d7c 728 if (WIFSIGNALED(Status) != 0)
40e7fe0e 729 {
ab7f4d7c
MV
730 if( WTERMSIG(Status) == SIGSEGV)
731 return _error->Error(_("Sub-process %s received a segmentation fault."),Name);
732 else
733 return _error->Error(_("Sub-process %s received signal %u."),Name, WTERMSIG(Status));
40e7fe0e 734 }
ddc1d8d0
AL
735
736 if (WIFEXITED(Status) != 0)
b2e465d6 737 return _error->Error(_("Sub-process %s returned an error code (%u)"),Name,WEXITSTATUS(Status));
ddc1d8d0 738
b2e465d6 739 return _error->Error(_("Sub-process %s exited unexpectedly"),Name);
ddc1d8d0
AL
740 }
741
742 return true;
743}
744 /*}}}*/
578bfd0a 745
13d87e2e 746// FileFd::Open - Open a file /*{{{*/
578bfd0a
AL
747// ---------------------------------------------------------------------
748/* The most commonly used open mode combinations are given with Mode */
52b47296 749bool FileFd::Open(string FileName,unsigned int const Mode,CompressMode Compress, unsigned long const Perms)
578bfd0a 750{
257e8d66
DK
751 if (Mode == ReadOnlyGzip)
752 return Open(FileName, ReadOnly, Gzip, Perms);
257e8d66 753
468720c5
DK
754 if (Compress == Auto && (Mode & WriteOnly) == WriteOnly)
755 return _error->Error("Autodetection on %s only works in ReadOnly openmode!", FileName.c_str());
257e8d66 756
468720c5
DK
757 // FIXME: Denote inbuilt compressors somehow - as we don't need to have the binaries for them
758 std::vector<APT::Configuration::Compressor> const compressors = APT::Configuration::getCompressors();
759 std::vector<APT::Configuration::Compressor>::const_iterator compressor = compressors.begin();
760 if (Compress == Auto)
761 {
468720c5
DK
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;
468720c5
DK
768 break;
769 }
770 }
771 else if (Compress == Extension)
772 {
52b47296
DK
773 std::string::size_type const found = FileName.find_last_of('.');
774 std::string ext;
775 if (found != std::string::npos)
776 {
777 ext = FileName.substr(found);
778 if (ext == ".new" || ext == ".bak")
779 {
780 std::string::size_type const found2 = FileName.find_last_of('.', found - 1);
781 if (found2 != std::string::npos)
782 ext = FileName.substr(found2, found - found2);
783 else
784 ext.clear();
785 }
786 }
aee1aac6
DK
787 for (; compressor != compressors.end(); ++compressor)
788 if (ext == compressor->Extension)
789 break;
790 // no matching extension - assume uncompressed (imagine files like 'example.org_Packages')
791 if (compressor == compressors.end())
792 for (compressor = compressors.begin(); compressor != compressors.end(); ++compressor)
793 if (compressor->Name == ".")
468720c5 794 break;
468720c5 795 }
aee1aac6 796 else
468720c5
DK
797 {
798 std::string name;
799 switch (Compress)
800 {
aee1aac6 801 case None: name = "."; break;
468720c5
DK
802 case Gzip: name = "gzip"; break;
803 case Bzip2: name = "bzip2"; break;
804 case Lzma: name = "lzma"; break;
805 case Xz: name = "xz"; break;
aee1aac6
DK
806 case Auto:
807 case Extension:
52b47296
DK
808 // Unreachable
809 return _error->Error("Opening File %s in None, Auto or Extension should be already handled?!?", FileName.c_str());
468720c5
DK
810 }
811 for (; compressor != compressors.end(); ++compressor)
812 if (compressor->Name == name)
813 break;
aee1aac6 814 if (compressor == compressors.end())
468720c5
DK
815 return _error->Error("Can't find a configured compressor %s for file %s", name.c_str(), FileName.c_str());
816 }
817
aee1aac6
DK
818 if (compressor == compressors.end())
819 return _error->Error("Can't find a match for specified compressor mode for file %s", FileName.c_str());
820 return Open(FileName, Mode, *compressor, Perms);
821}
52b47296 822bool FileFd::Open(string FileName,unsigned int const Mode,APT::Configuration::Compressor const &compressor, unsigned long const Perms)
aee1aac6
DK
823{
824 Close();
825 d = new FileFdPrivate;
826 d->openmode = Mode;
827 Flags = AutoClose;
828
829 if ((Mode & WriteOnly) != WriteOnly && (Mode & (Atomic | Create | Empty | Exclusive)) != 0)
830 return _error->Error("ReadOnly mode for %s doesn't accept additional flags!", FileName.c_str());
831 if ((Mode & ReadWrite) == 0)
832 return _error->Error("No openmode provided in FileFd::Open for %s", FileName.c_str());
468720c5 833
257e8d66
DK
834 if ((Mode & Atomic) == Atomic)
835 {
836 Flags |= Replace;
837 char *name = strdup((FileName + ".XXXXXX").c_str());
838 TemporaryFileName = string(mktemp(name));
839 free(name);
840 }
841 else if ((Mode & (Exclusive | Create)) == (Exclusive | Create))
842 {
843 // for atomic, this will be done by rename in Close()
844 unlink(FileName.c_str());
845 }
846 if ((Mode & Empty) == Empty)
578bfd0a 847 {
257e8d66
DK
848 struct stat Buf;
849 if (lstat(FileName.c_str(),&Buf) == 0 && S_ISLNK(Buf.st_mode))
850 unlink(FileName.c_str());
851 }
c4fc2fd7 852
561f860a
DK
853 int fileflags = 0;
854 #define if_FLAGGED_SET(FLAG, MODE) if ((Mode & FLAG) == FLAG) fileflags |= MODE
855 if_FLAGGED_SET(ReadWrite, O_RDWR);
856 else if_FLAGGED_SET(ReadOnly, O_RDONLY);
857 else if_FLAGGED_SET(WriteOnly, O_WRONLY);
4a9db827 858
561f860a
DK
859 if_FLAGGED_SET(Create, O_CREAT);
860 if_FLAGGED_SET(Empty, O_TRUNC);
861 if_FLAGGED_SET(Exclusive, O_EXCL);
862 else if_FLAGGED_SET(Atomic, O_EXCL);
863 #undef if_FLAGGED_SET
52b47296 864
561f860a
DK
865 if (TemporaryFileName.empty() == false)
866 iFd = open(TemporaryFileName.c_str(), fileflags, Perms);
468720c5 867 else
561f860a 868 iFd = open(FileName.c_str(), fileflags, Perms);
468720c5 869
561f860a
DK
870 if (iFd == -1 || OpenInternDescriptor(Mode, compressor) == false)
871 {
468720c5 872 if (iFd != -1)
fc81e8f2 873 {
561f860a
DK
874 close (iFd);
875 iFd = -1;
fc81e8f2 876 }
561f860a 877 return _error->Errno("open",_("Could not open file %s"), FileName.c_str());
257e8d66 878 }
578bfd0a 879
13d87e2e
AL
880 this->FileName = FileName;
881 SetCloseExec(iFd,true);
882 return true;
578bfd0a 883}
257e8d66
DK
884 /*}}}*/
885// FileFd::OpenDescriptor - Open a filedescriptor /*{{{*/
886// ---------------------------------------------------------------------
887/* */
52b47296 888bool FileFd::OpenDescriptor(int Fd, unsigned int const Mode, CompressMode Compress, bool AutoClose)
aee1aac6
DK
889{
890 std::vector<APT::Configuration::Compressor> const compressors = APT::Configuration::getCompressors();
891 std::vector<APT::Configuration::Compressor>::const_iterator compressor = compressors.begin();
892 std::string name;
893 switch (Compress)
894 {
895 case None: name = "."; break;
896 case Gzip: name = "gzip"; break;
897 case Bzip2: name = "bzip2"; break;
898 case Lzma: name = "lzma"; break;
899 case Xz: name = "xz"; break;
900 case Auto:
901 case Extension:
902 return _error->Error("Opening Fd %d in Auto or Extension compression mode is not supported", Fd);
903 }
904 for (; compressor != compressors.end(); ++compressor)
905 if (compressor->Name == name)
906 break;
907 if (compressor == compressors.end())
908 return _error->Error("Can't find a configured compressor %s for file %s", name.c_str(), FileName.c_str());
909
910 return OpenDescriptor(Fd, Mode, *compressor, AutoClose);
911}
52b47296 912bool FileFd::OpenDescriptor(int Fd, unsigned int const Mode, APT::Configuration::Compressor const &compressor, bool AutoClose)
144c0969
JAK
913{
914 Close();
032bd56f 915 d = new FileFdPrivate;
699b209e 916 d->openmode = Mode;
144c0969
JAK
917 Flags = (AutoClose) ? FileFd::AutoClose : 0;
918 iFd = Fd;
aee1aac6 919 if (OpenInternDescriptor(Mode, compressor) == false)
468720c5
DK
920 {
921 if (AutoClose)
922 close (iFd);
923 return _error->Errno("gzdopen",_("Could not open file descriptor %d"), Fd);
144c0969
JAK
924 }
925 this->FileName = "";
926 return true;
468720c5 927}
52b47296 928bool FileFd::OpenInternDescriptor(unsigned int const Mode, APT::Configuration::Compressor const &compressor)
468720c5 929{
561f860a
DK
930 d->compressor = compressor;
931 if (compressor.Name == "." || compressor.Binary.empty() == true)
468720c5 932 return true;
aee1aac6
DK
933#if APT_USE_ZLIB
934 else if (compressor.Name == "gzip")
468720c5
DK
935 {
936 if ((Mode & ReadWrite) == ReadWrite)
032bd56f 937 d->gz = gzdopen(iFd, "r+");
468720c5 938 else if ((Mode & WriteOnly) == WriteOnly)
032bd56f 939 d->gz = gzdopen(iFd, "w");
468720c5 940 else
032bd56f
DK
941 d->gz = gzdopen (iFd, "r");
942 if (d->gz == NULL)
468720c5 943 return false;
032bd56f 944 Flags |= Compressed;
561f860a 945 return true;
468720c5 946 }
699b209e 947#endif
561f860a
DK
948
949 if ((Mode & ReadWrite) == ReadWrite)
950 return _error->Error("ReadWrite mode is not supported for file %s", FileName.c_str());
951
952 bool const Comp = (Mode & WriteOnly) == WriteOnly;
953 // Handle 'decompression' of empty files
954 if (Comp == false)
955 {
956 struct stat Buf;
957 fstat(iFd, &Buf);
958 if (Buf.st_size == 0 && S_ISFIFO(Buf.st_mode) == false)
959 return true;
960
961 // We don't need the file open - instead let the compressor open it
962 // as he properly knows better how to efficiently read from 'his' file
963 if (FileName.empty() == false)
964 close(iFd);
965 }
966
967 // Create a data pipe
968 int Pipe[2] = {-1,-1};
969 if (pipe(Pipe) != 0)
970 return _error->Errno("pipe",_("Failed to create subprocess IPC"));
971 for (int J = 0; J != 2; J++)
972 SetCloseExec(Pipe[J],true);
973
974 d->compressed_fd = iFd;
975 d->pipe = true;
976
977 if (Comp == true)
978 iFd = Pipe[1];
979 else
980 iFd = Pipe[0];
981
982 // The child..
983 d->compressor_pid = ExecFork();
984 if (d->compressor_pid == 0)
985 {
986 if (Comp == true)
987 {
988 dup2(d->compressed_fd,STDOUT_FILENO);
989 dup2(Pipe[0],STDIN_FILENO);
990 }
991 else
992 {
993 if (FileName.empty() == true)
994 dup2(d->compressed_fd,STDIN_FILENO);
995 dup2(Pipe[1],STDOUT_FILENO);
996 }
997
998 SetCloseExec(STDOUT_FILENO,false);
999 SetCloseExec(STDIN_FILENO,false);
1000
1001 std::vector<char const*> Args;
1002 Args.push_back(compressor.Binary.c_str());
1003 std::vector<std::string> const * const addArgs =
1004 (Comp == true) ? &(compressor.CompressArgs) : &(compressor.UncompressArgs);
1005 for (std::vector<std::string>::const_iterator a = addArgs->begin();
1006 a != addArgs->end(); ++a)
1007 Args.push_back(a->c_str());
1008 if (Comp == false && FileName.empty() == false)
1009 {
1010 Args.push_back("--stdout");
1011 if (TemporaryFileName.empty() == false)
1012 Args.push_back(TemporaryFileName.c_str());
1013 else
1014 Args.push_back(FileName.c_str());
1015 }
1016 Args.push_back(NULL);
1017
1018 execvp(Args[0],(char **)&Args[0]);
1019 cerr << _("Failed to exec compressor ") << Args[0] << endl;
1020 _exit(100);
1021 }
1022 if (Comp == true)
1023 close(Pipe[0]);
468720c5 1024 else
561f860a
DK
1025 close(Pipe[1]);
1026 if (Comp == true || FileName.empty() == true)
1027 close(d->compressed_fd);
1028
468720c5 1029 return true;
144c0969 1030}
578bfd0a 1031 /*}}}*/
8e06abb2 1032// FileFd::~File - Closes the file /*{{{*/
578bfd0a
AL
1033// ---------------------------------------------------------------------
1034/* If the proper modes are selected then we close the Fd and possibly
1035 unlink the file on error. */
8e06abb2 1036FileFd::~FileFd()
578bfd0a
AL
1037{
1038 Close();
1039}
1040 /*}}}*/
8e06abb2 1041// FileFd::Read - Read a bit of the file /*{{{*/
578bfd0a 1042// ---------------------------------------------------------------------
b0db36b1
AL
1043/* We are carefull to handle interruption by a signal while reading
1044 gracefully. */
650faab0 1045bool FileFd::Read(void *To,unsigned long long Size,unsigned long long *Actual)
578bfd0a 1046{
b0db36b1
AL
1047 int Res;
1048 errno = 0;
f604cf55
AL
1049 if (Actual != 0)
1050 *Actual = 0;
699b209e 1051 *((char *)To) = '\0';
b0db36b1 1052 do
578bfd0a 1053 {
aee1aac6 1054#if APT_USE_ZLIB
032bd56f
DK
1055 if (d->gz != NULL)
1056 Res = gzread(d->gz,To,Size);
a3a03f5d 1057 else
699b209e 1058#endif
a3a03f5d 1059 Res = read(iFd,To,Size);
b0db36b1
AL
1060 if (Res < 0 && errno == EINTR)
1061 continue;
1062 if (Res < 0)
1063 {
1064 Flags |= Fail;
b2e465d6 1065 return _error->Errno("read",_("Read error"));
b0db36b1 1066 }
578bfd0a 1067
b0db36b1
AL
1068 To = (char *)To + Res;
1069 Size -= Res;
1abbc47c 1070 d->seekpos += Res;
f604cf55
AL
1071 if (Actual != 0)
1072 *Actual += Res;
b0db36b1
AL
1073 }
1074 while (Res > 0 && Size > 0);
1075
1076 if (Size == 0)
1077 return true;
1078
ddc1d8d0 1079 // Eof handling
f604cf55 1080 if (Actual != 0)
ddc1d8d0
AL
1081 {
1082 Flags |= HitEof;
1083 return true;
1084 }
1085
b0db36b1 1086 Flags |= Fail;
650faab0 1087 return _error->Error(_("read, still have %llu to read but none left"), Size);
578bfd0a
AL
1088}
1089 /*}}}*/
032bd56f
DK
1090// FileFd::ReadLine - Read a complete line from the file /*{{{*/
1091// ---------------------------------------------------------------------
1092/* Beware: This method can be quiet slow for big buffers on UNcompressed
1093 files because of the naive implementation! */
1094char* FileFd::ReadLine(char *To, unsigned long long const Size)
1095{
699b209e 1096 *To = '\0';
aee1aac6 1097#if APT_USE_ZLIB
032bd56f
DK
1098 if (d->gz != NULL)
1099 return gzgets(d->gz, To, Size);
699b209e 1100#endif
032bd56f
DK
1101
1102 unsigned long long read = 0;
1103 if (Read(To, Size, &read) == false)
1104 return NULL;
1105 char* c = To;
1106 for (; *c != '\n' && *c != '\0' && read != 0; --read, ++c)
1107 ; // find the end of the line
1108 if (*c != '\0')
1109 *c = '\0';
1110 if (read != 0)
1111 Seek(Tell() - read);
1112 return To;
1113}
1114 /*}}}*/
8e06abb2 1115// FileFd::Write - Write to the file /*{{{*/
578bfd0a
AL
1116// ---------------------------------------------------------------------
1117/* */
650faab0 1118bool FileFd::Write(const void *From,unsigned long long Size)
578bfd0a 1119{
b0db36b1
AL
1120 int Res;
1121 errno = 0;
1122 do
578bfd0a 1123 {
aee1aac6 1124#if APT_USE_ZLIB
032bd56f
DK
1125 if (d->gz != NULL)
1126 Res = gzwrite(d->gz,From,Size);
a3a03f5d 1127 else
699b209e 1128#endif
a3a03f5d 1129 Res = write(iFd,From,Size);
b0db36b1
AL
1130 if (Res < 0 && errno == EINTR)
1131 continue;
1132 if (Res < 0)
1133 {
1134 Flags |= Fail;
b2e465d6 1135 return _error->Errno("write",_("Write error"));
b0db36b1
AL
1136 }
1137
1138 From = (char *)From + Res;
1139 Size -= Res;
1abbc47c 1140 d->seekpos += Res;
578bfd0a 1141 }
b0db36b1 1142 while (Res > 0 && Size > 0);
578bfd0a 1143
b0db36b1
AL
1144 if (Size == 0)
1145 return true;
1146
1147 Flags |= Fail;
650faab0 1148 return _error->Error(_("write, still have %llu to write but couldn't"), Size);
578bfd0a
AL
1149}
1150 /*}}}*/
8e06abb2 1151// FileFd::Seek - Seek in the file /*{{{*/
578bfd0a
AL
1152// ---------------------------------------------------------------------
1153/* */
650faab0 1154bool FileFd::Seek(unsigned long long To)
578bfd0a 1155{
699b209e
DK
1156 if (d->pipe == true)
1157 {
1abbc47c
DK
1158 // Our poor man seeking in pipes is costly, so try to avoid it
1159 unsigned long long seekpos = Tell();
1160 if (seekpos == To)
1161 return true;
1162 else if (seekpos < To)
1163 return Skip(To - seekpos);
1164
561f860a
DK
1165 if ((d->openmode & ReadOnly) != ReadOnly)
1166 return _error->Error("Reopen is only implemented for read-only files!");
699b209e 1167 close(iFd);
6fd947bd 1168 iFd = 0;
561f860a
DK
1169 if (TemporaryFileName.empty() == false)
1170 iFd = open(TemporaryFileName.c_str(), O_RDONLY);
1171 else if (FileName.empty() == false)
1172 iFd = open(FileName.c_str(), O_RDONLY);
1173 else
6fd947bd
DK
1174 {
1175 if (d->compressed_fd > 0)
1176 if (lseek(d->compressed_fd, 0, SEEK_SET) != 0)
1177 iFd = d->compressed_fd;
1178 if (iFd <= 0)
1179 return _error->Error("Reopen is not implemented for pipes opened with FileFd::OpenDescriptor()!");
1180 }
561f860a
DK
1181
1182 if (OpenInternDescriptor(d->openmode, d->compressor) == false)
1183 return _error->Error("Seek on file %s because it couldn't be reopened", FileName.c_str());
1184
1185 if (To != 0)
1186 return Skip(To);
1abbc47c
DK
1187
1188 d->seekpos = To;
561f860a 1189 return true;
699b209e 1190 }
a3a03f5d 1191 int res;
aee1aac6 1192#if APT_USE_ZLIB
032bd56f
DK
1193 if (d->gz)
1194 res = gzseek(d->gz,To,SEEK_SET);
a3a03f5d 1195 else
699b209e 1196#endif
a3a03f5d 1197 res = lseek(iFd,To,SEEK_SET);
1198 if (res != (signed)To)
578bfd0a
AL
1199 {
1200 Flags |= Fail;
650faab0 1201 return _error->Error("Unable to seek to %llu", To);
578bfd0a 1202 }
1abbc47c
DK
1203
1204 d->seekpos = To;
727f18af
AL
1205 return true;
1206}
1207 /*}}}*/
1208// FileFd::Skip - Seek in the file /*{{{*/
1209// ---------------------------------------------------------------------
1210/* */
650faab0 1211bool FileFd::Skip(unsigned long long Over)
727f18af 1212{
a3a03f5d 1213 int res;
aee1aac6 1214#if APT_USE_ZLIB
032bd56f
DK
1215 if (d->gz != NULL)
1216 res = gzseek(d->gz,Over,SEEK_CUR);
a3a03f5d 1217 else
699b209e 1218#endif
a3a03f5d 1219 res = lseek(iFd,Over,SEEK_CUR);
1220 if (res < 0)
727f18af
AL
1221 {
1222 Flags |= Fail;
650faab0 1223 return _error->Error("Unable to seek ahead %llu",Over);
727f18af 1224 }
1abbc47c
DK
1225 d->seekpos = res;
1226
6d5dd02a
AL
1227 return true;
1228}
1229 /*}}}*/
1230// FileFd::Truncate - Truncate the file /*{{{*/
1231// ---------------------------------------------------------------------
1232/* */
650faab0 1233bool FileFd::Truncate(unsigned long long To)
6d5dd02a 1234{
032bd56f 1235 if (d->gz != NULL)
a3a03f5d 1236 {
1237 Flags |= Fail;
1238 return _error->Error("Truncating gzipped files is not implemented (%s)", FileName.c_str());
1239 }
6d5dd02a
AL
1240 if (ftruncate(iFd,To) != 0)
1241 {
1242 Flags |= Fail;
650faab0 1243 return _error->Error("Unable to truncate to %llu",To);
6d5dd02a
AL
1244 }
1245
578bfd0a
AL
1246 return true;
1247}
1248 /*}}}*/
7f25bdff
AL
1249// FileFd::Tell - Current seek position /*{{{*/
1250// ---------------------------------------------------------------------
1251/* */
650faab0 1252unsigned long long FileFd::Tell()
7f25bdff 1253{
1abbc47c
DK
1254 // In theory, we could just return seekpos here always instead of
1255 // seeking around, but not all users of FileFd use always Seek() and co
1256 // so d->seekpos isn't always true and we can just use it as a hint if
1257 // we have nothing else, but not always as an authority…
1258 if (d->pipe == true)
1259 return d->seekpos;
1260
a3a03f5d 1261 off_t Res;
aee1aac6 1262#if APT_USE_ZLIB
032bd56f
DK
1263 if (d->gz != NULL)
1264 Res = gztell(d->gz);
a3a03f5d 1265 else
699b209e 1266#endif
a3a03f5d 1267 Res = lseek(iFd,0,SEEK_CUR);
7f25bdff
AL
1268 if (Res == (off_t)-1)
1269 _error->Errno("lseek","Failed to determine the current file position");
1abbc47c 1270 d->seekpos = Res;
7f25bdff
AL
1271 return Res;
1272}
1273 /*}}}*/
4260fd39 1274// FileFd::FileSize - Return the size of the file /*{{{*/
578bfd0a
AL
1275// ---------------------------------------------------------------------
1276/* */
650faab0 1277unsigned long long FileFd::FileSize()
578bfd0a
AL
1278{
1279 struct stat Buf;
699b209e 1280 if (d->pipe == false && fstat(iFd,&Buf) != 0)
44dc669e 1281 return _error->Errno("fstat","Unable to determine the file size");
699b209e
DK
1282
1283 // for compressor pipes st_size is undefined and at 'best' zero
1284 if (d->pipe == true || S_ISFIFO(Buf.st_mode))
1285 {
1286 // we set it here, too, as we get the info here for free
1287 // in theory the Open-methods should take care of it already
1288 d->pipe = true;
1289 if (stat(FileName.c_str(), &Buf) != 0)
1290 return _error->Errno("stat","Unable to determine the file size");
1291 }
1292
4260fd39
DK
1293 return Buf.st_size;
1294}
1295 /*}}}*/
1296// FileFd::Size - Return the size of the content in the file /*{{{*/
1297// ---------------------------------------------------------------------
1298/* */
650faab0 1299unsigned long long FileFd::Size()
4260fd39 1300{
650faab0 1301 unsigned long long size = FileSize();
44dc669e 1302
699b209e
DK
1303 // for compressor pipes st_size is undefined and at 'best' zero,
1304 // so we 'read' the content and 'seek' back - see there
1305 if (d->pipe == true)
1306 {
1abbc47c 1307 unsigned long long const oldSeek = Tell();
699b209e
DK
1308 char ignore[1000];
1309 unsigned long long read = 0;
1310 do {
1311 Read(ignore, sizeof(ignore), &read);
699b209e 1312 } while(read != 0);
1abbc47c
DK
1313 size = Tell();
1314 Seek(oldSeek);
699b209e 1315 }
aee1aac6 1316#if APT_USE_ZLIB
44dc669e 1317 // only check gzsize if we are actually a gzip file, just checking for
032bd56f 1318 // "gz" is not sufficient as uncompressed files could be opened with
44dc669e 1319 // gzopen in "direct" mode as well
699b209e 1320 else if (d->gz && !gzdirect(d->gz) && size > 0)
9c182afa
MP
1321 {
1322 /* unfortunately zlib.h doesn't provide a gzsize(), so we have to do
1323 * this ourselves; the original (uncompressed) file size is the last 32
1324 * bits of the file */
650faab0 1325 // FIXME: Size for gz-files is limited by 32bit… no largefile support
9c182afa
MP
1326 if (lseek(iFd, -4, SEEK_END) < 0)
1327 return _error->Errno("lseek","Unable to seek to end of gzipped file");
f330c0f3 1328 size = 0L;
9c182afa
MP
1329 if (read(iFd, &size, 4) != 4)
1330 return _error->Errno("read","Unable to read original size of gzipped file");
f330c0f3
DK
1331
1332#ifdef WORDS_BIGENDIAN
2cae0ccb
DK
1333 uint32_t tmp_size = size;
1334 uint8_t const * const p = (uint8_t const * const) &tmp_size;
1335 tmp_size = (p[3] << 24) | (p[2] << 16) | (p[1] << 8) | p[0];
1336 size = tmp_size;
f330c0f3 1337#endif
9c182afa 1338
1abbc47c 1339 if (lseek(iFd, d->seekpos, SEEK_SET) < 0)
9c182afa
MP
1340 return _error->Errno("lseek","Unable to seek in gzipped file");
1341 return size;
1342 }
699b209e 1343#endif
9c182afa 1344
44dc669e 1345 return size;
578bfd0a
AL
1346}
1347 /*}}}*/
76a763e1
DK
1348// FileFd::ModificationTime - Return the time of last touch /*{{{*/
1349// ---------------------------------------------------------------------
1350/* */
1351time_t FileFd::ModificationTime()
1352{
1353 struct stat Buf;
699b209e 1354 if (d->pipe == false && fstat(iFd,&Buf) != 0)
76a763e1
DK
1355 {
1356 _error->Errno("fstat","Unable to determine the modification time of file %s", FileName.c_str());
1357 return 0;
1358 }
699b209e
DK
1359
1360 // for compressor pipes st_size is undefined and at 'best' zero
1361 if (d->pipe == true || S_ISFIFO(Buf.st_mode))
1362 {
1363 // we set it here, too, as we get the info here for free
1364 // in theory the Open-methods should take care of it already
1365 d->pipe = true;
1366 if (stat(FileName.c_str(), &Buf) != 0)
1367 {
1368 _error->Errno("fstat","Unable to determine the modification time of file %s", FileName.c_str());
1369 return 0;
1370 }
1371 }
1372
76a763e1
DK
1373 return Buf.st_mtime;
1374}
1375 /*}}}*/
8e06abb2 1376// FileFd::Close - Close the file if the close flag is set /*{{{*/
578bfd0a
AL
1377// ---------------------------------------------------------------------
1378/* */
8e06abb2 1379bool FileFd::Close()
578bfd0a 1380{
032bd56f
DK
1381 if (iFd == -1)
1382 return true;
1383
578bfd0a
AL
1384 bool Res = true;
1385 if ((Flags & AutoClose) == AutoClose)
d13c2d3f 1386 {
aee1aac6 1387#if APT_USE_ZLIB
032bd56f
DK
1388 if (d != NULL && d->gz != NULL) {
1389 int const e = gzclose(d->gz);
d13c2d3f 1390 // gzdopen() on empty files always fails with "buffer error" here, ignore that
1391 if (e != 0 && e != Z_BUF_ERROR)
3184b4cf 1392 Res &= _error->Errno("close",_("Problem closing the gzip file %s"), FileName.c_str());
d13c2d3f 1393 } else
699b209e 1394#endif
d13c2d3f 1395 if (iFd > 0 && close(iFd) != 0)
3184b4cf 1396 Res &= _error->Errno("close",_("Problem closing the file %s"), FileName.c_str());
d13c2d3f 1397 }
3010fb0e 1398
62d073d9 1399 if ((Flags & Replace) == Replace && iFd >= 0) {
3010fb0e 1400 if (rename(TemporaryFileName.c_str(), FileName.c_str()) != 0)
62d073d9
DK
1401 Res &= _error->Errno("rename",_("Problem renaming the file %s to %s"), TemporaryFileName.c_str(), FileName.c_str());
1402
fd3b761e 1403 FileName = TemporaryFileName; // for the unlink() below.
257e8d66 1404 TemporaryFileName.clear();
3010fb0e 1405 }
62d073d9
DK
1406
1407 iFd = -1;
1408
578bfd0a
AL
1409 if ((Flags & Fail) == Fail && (Flags & DelOnFail) == DelOnFail &&
1410 FileName.empty() == false)
1411 if (unlink(FileName.c_str()) != 0)
62d073d9 1412 Res &= _error->WarningE("unlnk",_("Problem unlinking the file %s"), FileName.c_str());
3010fb0e 1413
032bd56f
DK
1414 if (d != NULL)
1415 {
561f860a 1416 if (d->compressor_pid > 0)
52b47296 1417 ExecWait(d->compressor_pid, "FileFdCompressor", true);
032bd56f
DK
1418 delete d;
1419 d = NULL;
1420 }
3010fb0e 1421
578bfd0a
AL
1422 return Res;
1423}
1424 /*}}}*/
b2e465d6
AL
1425// FileFd::Sync - Sync the file /*{{{*/
1426// ---------------------------------------------------------------------
1427/* */
1428bool FileFd::Sync()
1429{
1430#ifdef _POSIX_SYNCHRONIZED_IO
1431 if (fsync(iFd) != 0)
1432 return _error->Errno("sync",_("Problem syncing the file"));
1433#endif
1434 return true;
1435}
1436 /*}}}*/
699b209e
DK
1437
1438gzFile FileFd::gzFd() { return (gzFile) d->gz; }