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