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