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