Support large files in the complete toolset. Indexes of this
[ntk/apt.git] / apt-pkg / contrib / fileutl.cc
CommitLineData
578bfd0a
AL
1// -*- mode: cpp; mode: fold -*-
2// Description /*{{{*/
7da2b375 3// $Id: fileutl.cc,v 1.42 2002/09/14 05:29:22 jgg Exp $
578bfd0a
AL
4/* ######################################################################
5
6 File Utilities
7
8 CopyFile - Buffered copy of a single file
9 GetLock - dpkg compatible lock file manipulation (fcntl)
10
614adaa0
MV
11 Most of this source is placed in the Public Domain, do with it what
12 you will
7da2b375 13 It was originally written by Jason Gunthorpe <jgg@debian.org>.
a3a03f5d 14 FileFd gzip support added by Martin Pitt <martin.pitt@canonical.com>
578bfd0a 15
614adaa0
MV
16 The exception is RunScripts() it is under the GPLv2
17
578bfd0a
AL
18 ##################################################################### */
19 /*}}}*/
20// Include Files /*{{{*/
ea542140
DK
21#include <config.h>
22
094a497d 23#include <apt-pkg/fileutl.h>
1cd1c398 24#include <apt-pkg/strutl.h>
094a497d 25#include <apt-pkg/error.h>
b2e465d6 26#include <apt-pkg/sptr.h>
75ef8f14 27#include <apt-pkg/configuration.h>
b2e465d6 28
152ab79e 29#include <cstdlib>
4f333a8b 30#include <cstring>
3010fb0e 31#include <cstdio>
4f333a8b 32
4d055c05 33#include <iostream>
578bfd0a 34#include <unistd.h>
2c206aa4 35#include <fcntl.h>
578bfd0a 36#include <sys/stat.h>
578bfd0a 37#include <sys/types.h>
cc2313b7 38#include <sys/time.h>
1ae93c94 39#include <sys/wait.h>
46e39c8e 40#include <dirent.h>
54676e1a 41#include <signal.h>
65a1e968 42#include <errno.h>
75ef8f14 43#include <set>
46e39c8e 44#include <algorithm>
2cae0ccb 45
2a79d5b5 46#ifdef WORDS_BIGENDIAN
2cae0ccb
DK
47#include <inttypes.h>
48#endif
ea542140
DK
49
50#include <apti18n.h>
578bfd0a
AL
51 /*}}}*/
52
4d055c05
AL
53using namespace std;
54
614adaa0
MV
55// RunScripts - Run a set of scripts from a configuration subtree /*{{{*/
56// ---------------------------------------------------------------------
57/* */
58bool RunScripts(const char *Cnf)
59{
60 Configuration::Item const *Opts = _config->Tree(Cnf);
61 if (Opts == 0 || Opts->Child == 0)
62 return true;
63 Opts = Opts->Child;
64
65 // Fork for running the system calls
66 pid_t Child = ExecFork();
67
68 // This is the child
69 if (Child == 0)
70 {
cfba4f69
MV
71 if (_config->FindDir("DPkg::Chroot-Directory","/") != "/")
72 {
73 std::cerr << "Chrooting into "
74 << _config->FindDir("DPkg::Chroot-Directory")
75 << std::endl;
76 if (chroot(_config->FindDir("DPkg::Chroot-Directory","/").c_str()) != 0)
77 _exit(100);
78 }
79
614adaa0
MV
80 if (chdir("/tmp/") != 0)
81 _exit(100);
82
83 unsigned int Count = 1;
84 for (; Opts != 0; Opts = Opts->Next, Count++)
85 {
86 if (Opts->Value.empty() == true)
87 continue;
88
89 if (system(Opts->Value.c_str()) != 0)
90 _exit(100+Count);
91 }
92 _exit(0);
93 }
94
95 // Wait for the child
96 int Status = 0;
97 while (waitpid(Child,&Status,0) != Child)
98 {
99 if (errno == EINTR)
100 continue;
101 return _error->Errno("waitpid","Couldn't wait for subprocess");
102 }
103
104 // Restore sig int/quit
105 signal(SIGQUIT,SIG_DFL);
106 signal(SIGINT,SIG_DFL);
107
108 // Check for an error code.
109 if (WIFEXITED(Status) == 0 || WEXITSTATUS(Status) != 0)
110 {
111 unsigned int Count = WEXITSTATUS(Status);
112 if (Count > 100)
113 {
114 Count -= 100;
115 for (; Opts != 0 && Count != 1; Opts = Opts->Next, Count--);
116 _error->Error("Problem executing scripts %s '%s'",Cnf,Opts->Value.c_str());
117 }
118
119 return _error->Error("Sub-process returned an error code");
120 }
121
122 return true;
123}
124 /*}}}*/
125
578bfd0a
AL
126// CopyFile - Buffered copy of a file /*{{{*/
127// ---------------------------------------------------------------------
128/* The caller is expected to set things so that failure causes erasure */
8b89e57f 129bool CopyFile(FileFd &From,FileFd &To)
578bfd0a
AL
130{
131 if (From.IsOpen() == false || To.IsOpen() == false)
132 return false;
133
134 // Buffered copy between fds
b2e465d6 135 SPtrArray<unsigned char> Buf = new unsigned char[64000];
650faab0 136 unsigned long long Size = From.Size();
b0db36b1 137 while (Size != 0)
578bfd0a 138 {
650faab0 139 unsigned long long ToRead = Size;
b0db36b1
AL
140 if (Size > 64000)
141 ToRead = 64000;
142
4a6d5862 143 if (From.Read(Buf,ToRead) == false ||
b0db36b1 144 To.Write(Buf,ToRead) == false)
578bfd0a 145 return false;
b0db36b1
AL
146
147 Size -= ToRead;
578bfd0a
AL
148 }
149
578bfd0a
AL
150 return true;
151}
152 /*}}}*/
153// GetLock - Gets a lock file /*{{{*/
154// ---------------------------------------------------------------------
155/* This will create an empty file of the given name and lock it. Once this
156 is done all other calls to GetLock in any other process will fail with
157 -1. The return result is the fd of the file, the call should call
158 close at some time. */
159int GetLock(string File,bool Errors)
160{
f659b39a
OS
161 // GetLock() is used in aptitude on directories with public-write access
162 // Use O_NOFOLLOW here to prevent symlink traversal attacks
163 int FD = open(File.c_str(),O_RDWR | O_CREAT | O_NOFOLLOW,0640);
578bfd0a
AL
164 if (FD < 0)
165 {
b2e465d6
AL
166 // Read only .. cant have locking problems there.
167 if (errno == EROFS)
168 {
169 _error->Warning(_("Not using locking for read only lock file %s"),File.c_str());
170 return dup(0); // Need something for the caller to close
171 }
172
578bfd0a 173 if (Errors == true)
b2e465d6
AL
174 _error->Errno("open",_("Could not open lock file %s"),File.c_str());
175
176 // Feh.. We do this to distinguish the lock vs open case..
177 errno = EPERM;
578bfd0a
AL
178 return -1;
179 }
b2e465d6
AL
180 SetCloseExec(FD,true);
181
578bfd0a
AL
182 // Aquire a write lock
183 struct flock fl;
c71bc556
AL
184 fl.l_type = F_WRLCK;
185 fl.l_whence = SEEK_SET;
186 fl.l_start = 0;
187 fl.l_len = 0;
578bfd0a
AL
188 if (fcntl(FD,F_SETLK,&fl) == -1)
189 {
d89df07a
AL
190 if (errno == ENOLCK)
191 {
b2e465d6
AL
192 _error->Warning(_("Not using locking for nfs mounted lock file %s"),File.c_str());
193 return dup(0); // Need something for the caller to close
d89df07a 194 }
578bfd0a 195 if (Errors == true)
b2e465d6
AL
196 _error->Errno("open",_("Could not get lock %s"),File.c_str());
197
198 int Tmp = errno;
578bfd0a 199 close(FD);
b2e465d6 200 errno = Tmp;
578bfd0a
AL
201 return -1;
202 }
203
204 return FD;
205}
206 /*}}}*/
207// FileExists - Check if a file exists /*{{{*/
208// ---------------------------------------------------------------------
36f1098a 209/* Beware: Directories are also files! */
578bfd0a
AL
210bool FileExists(string File)
211{
212 struct stat Buf;
213 if (stat(File.c_str(),&Buf) != 0)
214 return false;
215 return true;
216}
217 /*}}}*/
36f1098a
DK
218// RealFileExists - Check if a file exists and if it is really a file /*{{{*/
219// ---------------------------------------------------------------------
220/* */
221bool RealFileExists(string File)
222{
223 struct stat Buf;
224 if (stat(File.c_str(),&Buf) != 0)
225 return false;
226 return ((Buf.st_mode & S_IFREG) != 0);
227}
228 /*}}}*/
1cd1c398
DK
229// DirectoryExists - Check if a directory exists and is really one /*{{{*/
230// ---------------------------------------------------------------------
231/* */
232bool DirectoryExists(string const &Path)
233{
234 struct stat Buf;
235 if (stat(Path.c_str(),&Buf) != 0)
236 return false;
237 return ((Buf.st_mode & S_IFDIR) != 0);
238}
239 /*}}}*/
240// CreateDirectory - poor man's mkdir -p guarded by a parent directory /*{{{*/
241// ---------------------------------------------------------------------
242/* This method will create all directories needed for path in good old
243 mkdir -p style but refuses to do this if Parent is not a prefix of
244 this Path. Example: /var/cache/ and /var/cache/apt/archives are given,
245 so it will create apt/archives if /var/cache exists - on the other
246 hand if the parent is /var/lib the creation will fail as this path
247 is not a parent of the path to be generated. */
248bool CreateDirectory(string const &Parent, string const &Path)
249{
250 if (Parent.empty() == true || Path.empty() == true)
251 return false;
252
253 if (DirectoryExists(Path) == true)
254 return true;
255
256 if (DirectoryExists(Parent) == false)
257 return false;
258
259 // we are not going to create directories "into the blue"
260 if (Path.find(Parent, 0) != 0)
261 return false;
262
263 vector<string> const dirs = VectorizeString(Path.substr(Parent.size()), '/');
264 string progress = Parent;
265 for (vector<string>::const_iterator d = dirs.begin(); d != dirs.end(); ++d)
266 {
267 if (d->empty() == true)
268 continue;
269
270 progress.append("/").append(*d);
271 if (DirectoryExists(progress) == true)
272 continue;
273
274 if (mkdir(progress.c_str(), 0755) != 0)
275 return false;
276 }
277 return true;
278}
279 /*}}}*/
7753e468 280// CreateAPTDirectoryIfNeeded - ensure that the given directory exists /*{{{*/
b29c3712
DK
281// ---------------------------------------------------------------------
282/* a small wrapper around CreateDirectory to check if it exists and to
283 remove the trailing "/apt/" from the parent directory if needed */
7753e468 284bool CreateAPTDirectoryIfNeeded(string const &Parent, string const &Path)
b29c3712
DK
285{
286 if (DirectoryExists(Path) == true)
287 return true;
288
289 size_t const len = Parent.size();
290 if (len > 5 && Parent.find("/apt/", len - 6, 5) == len - 5)
291 {
292 if (CreateDirectory(Parent.substr(0,len-5), Path) == true)
293 return true;
294 }
295 else if (CreateDirectory(Parent, Path) == true)
296 return true;
297
298 return false;
299}
300 /*}}}*/
46e39c8e
MV
301// GetListOfFilesInDir - returns a vector of files in the given dir /*{{{*/
302// ---------------------------------------------------------------------
303/* If an extension is given only files with this extension are included
304 in the returned vector, otherwise every "normal" file is included. */
b39c1859
MV
305std::vector<string> GetListOfFilesInDir(string const &Dir, string const &Ext,
306 bool const &SortList, bool const &AllowNoExt)
307{
308 std::vector<string> ext;
309 ext.reserve(2);
310 if (Ext.empty() == false)
311 ext.push_back(Ext);
312 if (AllowNoExt == true && ext.empty() == false)
313 ext.push_back("");
314 return GetListOfFilesInDir(Dir, ext, SortList);
315}
316std::vector<string> GetListOfFilesInDir(string const &Dir, std::vector<string> const &Ext,
317 bool const &SortList)
318{
319 // Attention debuggers: need to be set with the environment config file!
320 bool const Debug = _config->FindB("Debug::GetListOfFilesInDir", false);
321 if (Debug == true)
322 {
323 std::clog << "Accept in " << Dir << " only files with the following " << Ext.size() << " extensions:" << std::endl;
324 if (Ext.empty() == true)
325 std::clog << "\tNO extension" << std::endl;
326 else
327 for (std::vector<string>::const_iterator e = Ext.begin();
328 e != Ext.end(); ++e)
329 std::clog << '\t' << (e->empty() == true ? "NO" : *e) << " extension" << std::endl;
330 }
331
46e39c8e 332 std::vector<string> List;
36f1098a
DK
333
334 if (DirectoryExists(Dir.c_str()) == false)
335 {
336 _error->Error(_("List of files can't be created as '%s' is not a directory"), Dir.c_str());
337 return List;
338 }
339
1408e219 340 Configuration::MatchAgainstConfig SilentIgnore("Dir::Ignore-Files-Silently");
46e39c8e
MV
341 DIR *D = opendir(Dir.c_str());
342 if (D == 0)
343 {
344 _error->Errno("opendir",_("Unable to read %s"),Dir.c_str());
345 return List;
346 }
347
348 for (struct dirent *Ent = readdir(D); Ent != 0; Ent = readdir(D))
349 {
b39c1859 350 // skip "hidden" files
46e39c8e
MV
351 if (Ent->d_name[0] == '.')
352 continue;
353
491058e3
DK
354 // Make sure it is a file and not something else
355 string const File = flCombine(Dir,Ent->d_name);
356#ifdef _DIRENT_HAVE_D_TYPE
357 if (Ent->d_type != DT_REG)
358#endif
359 {
360 if (RealFileExists(File.c_str()) == false)
361 {
362 if (SilentIgnore.Match(Ent->d_name) == false)
363 _error->Notice(_("Ignoring '%s' in directory '%s' as it is not a regular file"), Ent->d_name, Dir.c_str());
364 continue;
365 }
366 }
367
b39c1859
MV
368 // check for accepted extension:
369 // no extension given -> periods are bad as hell!
370 // extensions given -> "" extension allows no extension
371 if (Ext.empty() == false)
372 {
373 string d_ext = flExtension(Ent->d_name);
374 if (d_ext == Ent->d_name) // no extension
375 {
376 if (std::find(Ext.begin(), Ext.end(), "") == Ext.end())
377 {
378 if (Debug == true)
379 std::clog << "Bad file: " << Ent->d_name << " → no extension" << std::endl;
5edc3966 380 if (SilentIgnore.Match(Ent->d_name) == false)
491058e3 381 _error->Notice(_("Ignoring file '%s' in directory '%s' as it has no filename extension"), Ent->d_name, Dir.c_str());
b39c1859
MV
382 continue;
383 }
384 }
385 else if (std::find(Ext.begin(), Ext.end(), d_ext) == Ext.end())
386 {
387 if (Debug == true)
388 std::clog << "Bad file: " << Ent->d_name << " → bad extension »" << flExtension(Ent->d_name) << "«" << std::endl;
1408e219 389 if (SilentIgnore.Match(Ent->d_name) == false)
491058e3 390 _error->Notice(_("Ignoring file '%s' in directory '%s' as it has an invalid filename extension"), Ent->d_name, Dir.c_str());
b39c1859
MV
391 continue;
392 }
393 }
46e39c8e 394
b39c1859 395 // Skip bad filenames ala run-parts
46e39c8e
MV
396 const char *C = Ent->d_name;
397 for (; *C != 0; ++C)
398 if (isalpha(*C) == 0 && isdigit(*C) == 0
b39c1859
MV
399 && *C != '_' && *C != '-') {
400 // no required extension -> dot is a bad character
401 if (*C == '.' && Ext.empty() == false)
402 continue;
46e39c8e 403 break;
b39c1859 404 }
46e39c8e 405
b39c1859 406 // we don't reach the end of the name -> bad character included
46e39c8e 407 if (*C != 0)
b39c1859
MV
408 {
409 if (Debug == true)
410 std::clog << "Bad file: " << Ent->d_name << " → bad character »"
411 << *C << "« in filename (period allowed: " << (Ext.empty() ? "no" : "yes") << ")" << std::endl;
412 continue;
413 }
414
415 // skip filenames which end with a period. These are never valid
416 if (*(C - 1) == '.')
417 {
418 if (Debug == true)
419 std::clog << "Bad file: " << Ent->d_name << " → Period as last character" << std::endl;
46e39c8e 420 continue;
b39c1859 421 }
46e39c8e 422
b39c1859
MV
423 if (Debug == true)
424 std::clog << "Accept file: " << Ent->d_name << " in " << Dir << std::endl;
46e39c8e
MV
425 List.push_back(File);
426 }
427 closedir(D);
428
429 if (SortList == true)
430 std::sort(List.begin(),List.end());
431 return List;
432}
433 /*}}}*/
578bfd0a
AL
434// SafeGetCWD - This is a safer getcwd that returns a dynamic string /*{{{*/
435// ---------------------------------------------------------------------
436/* We return / on failure. */
437string SafeGetCWD()
438{
439 // Stash the current dir.
440 char S[300];
441 S[0] = 0;
7f25bdff 442 if (getcwd(S,sizeof(S)-2) == 0)
578bfd0a 443 return "/";
7f25bdff
AL
444 unsigned int Len = strlen(S);
445 S[Len] = '/';
446 S[Len+1] = 0;
578bfd0a
AL
447 return S;
448}
449 /*}}}*/
8ce4327b
AL
450// flNotDir - Strip the directory from the filename /*{{{*/
451// ---------------------------------------------------------------------
452/* */
453string flNotDir(string File)
454{
455 string::size_type Res = File.rfind('/');
456 if (Res == string::npos)
457 return File;
458 Res++;
459 return string(File,Res,Res - File.length());
460}
461 /*}}}*/
d38b7b3d
AL
462// flNotFile - Strip the file from the directory name /*{{{*/
463// ---------------------------------------------------------------------
171c45bc 464/* Result ends in a / */
d38b7b3d
AL
465string flNotFile(string File)
466{
467 string::size_type Res = File.rfind('/');
468 if (Res == string::npos)
171c45bc 469 return "./";
d38b7b3d
AL
470 Res++;
471 return string(File,0,Res);
472}
473 /*}}}*/
b2e465d6
AL
474// flExtension - Return the extension for the file /*{{{*/
475// ---------------------------------------------------------------------
476/* */
477string flExtension(string File)
478{
479 string::size_type Res = File.rfind('.');
480 if (Res == string::npos)
481 return File;
482 Res++;
483 return string(File,Res,Res - File.length());
484}
485 /*}}}*/
421c8d10
AL
486// flNoLink - If file is a symlink then deref it /*{{{*/
487// ---------------------------------------------------------------------
488/* If the name is not a link then the returned path is the input. */
489string flNoLink(string File)
490{
491 struct stat St;
492 if (lstat(File.c_str(),&St) != 0 || S_ISLNK(St.st_mode) == 0)
493 return File;
494 if (stat(File.c_str(),&St) != 0)
495 return File;
496
497 /* Loop resolving the link. There is no need to limit the number of
498 loops because the stat call above ensures that the symlink is not
499 circular */
500 char Buffer[1024];
501 string NFile = File;
502 while (1)
503 {
504 // Read the link
505 int Res;
506 if ((Res = readlink(NFile.c_str(),Buffer,sizeof(Buffer))) <= 0 ||
507 (unsigned)Res >= sizeof(Buffer))
508 return File;
509
510 // Append or replace the previous path
511 Buffer[Res] = 0;
512 if (Buffer[0] == '/')
513 NFile = Buffer;
514 else
515 NFile = flNotFile(NFile) + Buffer;
516
517 // See if we are done
518 if (lstat(NFile.c_str(),&St) != 0)
519 return File;
520 if (S_ISLNK(St.st_mode) == 0)
521 return NFile;
522 }
523}
524 /*}}}*/
b2e465d6
AL
525// flCombine - Combine a file and a directory /*{{{*/
526// ---------------------------------------------------------------------
527/* If the file is an absolute path then it is just returned, otherwise
528 the directory is pre-pended to it. */
529string flCombine(string Dir,string File)
530{
531 if (File.empty() == true)
532 return string();
533
534 if (File[0] == '/' || Dir.empty() == true)
535 return File;
536 if (File.length() >= 2 && File[0] == '.' && File[1] == '/')
537 return File;
538 if (Dir[Dir.length()-1] == '/')
539 return Dir + File;
540 return Dir + '/' + File;
541}
542 /*}}}*/
3b5421b4
AL
543// SetCloseExec - Set the close on exec flag /*{{{*/
544// ---------------------------------------------------------------------
545/* */
546void SetCloseExec(int Fd,bool Close)
547{
548 if (fcntl(Fd,F_SETFD,(Close == false)?0:FD_CLOEXEC) != 0)
549 {
550 cerr << "FATAL -> Could not set close on exec " << strerror(errno) << endl;
551 exit(100);
552 }
553}
554 /*}}}*/
555// SetNonBlock - Set the nonblocking flag /*{{{*/
556// ---------------------------------------------------------------------
557/* */
558void SetNonBlock(int Fd,bool Block)
559{
0a8a80e5
AL
560 int Flags = fcntl(Fd,F_GETFL) & (~O_NONBLOCK);
561 if (fcntl(Fd,F_SETFL,Flags | ((Block == false)?0:O_NONBLOCK)) != 0)
3b5421b4
AL
562 {
563 cerr << "FATAL -> Could not set non-blocking flag " << strerror(errno) << endl;
564 exit(100);
565 }
566}
567 /*}}}*/
568// WaitFd - Wait for a FD to become readable /*{{{*/
569// ---------------------------------------------------------------------
b2e465d6 570/* This waits for a FD to become readable using select. It is useful for
6d5dd02a
AL
571 applications making use of non-blocking sockets. The timeout is
572 in seconds. */
1084d58a 573bool WaitFd(int Fd,bool write,unsigned long timeout)
3b5421b4
AL
574{
575 fd_set Set;
cc2313b7 576 struct timeval tv;
3b5421b4
AL
577 FD_ZERO(&Set);
578 FD_SET(Fd,&Set);
6d5dd02a
AL
579 tv.tv_sec = timeout;
580 tv.tv_usec = 0;
1084d58a 581 if (write == true)
b0db36b1
AL
582 {
583 int Res;
584 do
585 {
586 Res = select(Fd+1,0,&Set,0,(timeout != 0?&tv:0));
587 }
588 while (Res < 0 && errno == EINTR);
589
590 if (Res <= 0)
591 return false;
1084d58a
AL
592 }
593 else
594 {
b0db36b1
AL
595 int Res;
596 do
597 {
598 Res = select(Fd+1,&Set,0,0,(timeout != 0?&tv:0));
599 }
600 while (Res < 0 && errno == EINTR);
601
602 if (Res <= 0)
603 return false;
cc2313b7 604 }
1084d58a 605
3b5421b4
AL
606 return true;
607}
608 /*}}}*/
54676e1a
AL
609// ExecFork - Magical fork that sanitizes the context before execing /*{{{*/
610// ---------------------------------------------------------------------
611/* This is used if you want to cleanse the environment for the forked
612 child, it fixes up the important signals and nukes all of the fds,
613 otherwise acts like normal fork. */
75ef8f14 614pid_t ExecFork()
54676e1a
AL
615{
616 // Fork off the process
617 pid_t Process = fork();
618 if (Process < 0)
619 {
620 cerr << "FATAL -> Failed to fork." << endl;
621 exit(100);
622 }
623
624 // Spawn the subprocess
625 if (Process == 0)
626 {
627 // Setup the signals
628 signal(SIGPIPE,SIG_DFL);
629 signal(SIGQUIT,SIG_DFL);
630 signal(SIGINT,SIG_DFL);
631 signal(SIGWINCH,SIG_DFL);
632 signal(SIGCONT,SIG_DFL);
633 signal(SIGTSTP,SIG_DFL);
75ef8f14
MV
634
635 set<int> KeepFDs;
636 Configuration::Item const *Opts = _config->Tree("APT::Keep-Fds");
637 if (Opts != 0 && Opts->Child != 0)
638 {
639 Opts = Opts->Child;
640 for (; Opts != 0; Opts = Opts->Next)
641 {
642 if (Opts->Value.empty() == true)
643 continue;
644 int fd = atoi(Opts->Value.c_str());
645 KeepFDs.insert(fd);
646 }
647 }
648
54676e1a
AL
649 // Close all of our FDs - just in case
650 for (int K = 3; K != 40; K++)
75ef8f14
MV
651 {
652 if(KeepFDs.find(K) == KeepFDs.end())
007dc9e0 653 fcntl(K,F_SETFD,FD_CLOEXEC);
75ef8f14 654 }
54676e1a
AL
655 }
656
657 return Process;
658}
659 /*}}}*/
ddc1d8d0
AL
660// ExecWait - Fancy waitpid /*{{{*/
661// ---------------------------------------------------------------------
2c9a72d1 662/* Waits for the given sub process. If Reap is set then no errors are
ddc1d8d0
AL
663 generated. Otherwise a failed subprocess will generate a proper descriptive
664 message */
3826564e 665bool ExecWait(pid_t Pid,const char *Name,bool Reap)
ddc1d8d0
AL
666{
667 if (Pid <= 1)
668 return true;
669
670 // Wait and collect the error code
671 int Status;
672 while (waitpid(Pid,&Status,0) != Pid)
673 {
674 if (errno == EINTR)
675 continue;
676
677 if (Reap == true)
678 return false;
679
db0db9fe 680 return _error->Error(_("Waited for %s but it wasn't there"),Name);
ddc1d8d0
AL
681 }
682
683
684 // Check for an error code.
685 if (WIFEXITED(Status) == 0 || WEXITSTATUS(Status) != 0)
686 {
687 if (Reap == true)
688 return false;
ab7f4d7c 689 if (WIFSIGNALED(Status) != 0)
40e7fe0e 690 {
ab7f4d7c
MV
691 if( WTERMSIG(Status) == SIGSEGV)
692 return _error->Error(_("Sub-process %s received a segmentation fault."),Name);
693 else
694 return _error->Error(_("Sub-process %s received signal %u."),Name, WTERMSIG(Status));
40e7fe0e 695 }
ddc1d8d0
AL
696
697 if (WIFEXITED(Status) != 0)
b2e465d6 698 return _error->Error(_("Sub-process %s returned an error code (%u)"),Name,WEXITSTATUS(Status));
ddc1d8d0 699
b2e465d6 700 return _error->Error(_("Sub-process %s exited unexpectedly"),Name);
ddc1d8d0
AL
701 }
702
703 return true;
704}
705 /*}}}*/
578bfd0a 706
13d87e2e 707// FileFd::Open - Open a file /*{{{*/
578bfd0a
AL
708// ---------------------------------------------------------------------
709/* The most commonly used open mode combinations are given with Mode */
13d87e2e 710bool FileFd::Open(string FileName,OpenMode Mode, unsigned long Perms)
578bfd0a 711{
13d87e2e 712 Close();
1164783d 713 Flags = AutoClose;
578bfd0a
AL
714 switch (Mode)
715 {
716 case ReadOnly:
717 iFd = open(FileName.c_str(),O_RDONLY);
718 break;
c4fc2fd7 719
720 case ReadOnlyGzip:
721 iFd = open(FileName.c_str(),O_RDONLY);
d13c2d3f 722 if (iFd > 0) {
c4fc2fd7 723 gz = gzdopen (iFd, "r");
724 if (gz == NULL) {
725 close (iFd);
726 iFd = -1;
727 }
a3a03f5d 728 }
578bfd0a
AL
729 break;
730
4a9db827 731 case WriteAtomic:
50b513a1 732 {
3010fb0e
JAK
733 Flags |= Replace;
734 char *name = strdup((FileName + ".XXXXXX").c_str());
735 TemporaryFileName = string(mktemp(name));
736 iFd = open(TemporaryFileName.c_str(),O_RDWR | O_CREAT | O_EXCL,Perms);
737 free(name);
50b513a1
AL
738 break;
739 }
4a9db827 740
fc81e8f2
JAK
741 case WriteEmpty:
742 {
743 struct stat Buf;
744 if (lstat(FileName.c_str(),&Buf) == 0 && S_ISLNK(Buf.st_mode))
745 unlink(FileName.c_str());
746 iFd = open(FileName.c_str(),O_RDWR | O_CREAT | O_TRUNC,Perms);
747 break;
748 }
578bfd0a
AL
749
750 case WriteExists:
751 iFd = open(FileName.c_str(),O_RDWR);
752 break;
0a8e3465
AL
753
754 case WriteAny:
755 iFd = open(FileName.c_str(),O_RDWR | O_CREAT,Perms);
d38b7b3d 756 break;
f08fcf34
AL
757
758 case WriteTemp:
4decd43c
AL
759 unlink(FileName.c_str());
760 iFd = open(FileName.c_str(),O_RDWR | O_CREAT | O_EXCL,Perms);
f08fcf34 761 break;
578bfd0a
AL
762 }
763
764 if (iFd < 0)
b2e465d6 765 return _error->Errno("open",_("Could not open file %s"),FileName.c_str());
13d87e2e
AL
766
767 this->FileName = FileName;
768 SetCloseExec(iFd,true);
769 return true;
578bfd0a 770}
144c0969
JAK
771
772bool FileFd::OpenDescriptor(int Fd, OpenMode Mode, bool AutoClose)
773{
774 Close();
775 Flags = (AutoClose) ? FileFd::AutoClose : 0;
776 iFd = Fd;
777 if (Mode == ReadOnlyGzip) {
778 gz = gzdopen (iFd, "r");
779 if (gz == NULL) {
780 if (AutoClose)
781 close (iFd);
782 return _error->Errno("gzdopen",_("Could not open file descriptor %d"),
783 Fd);
784 }
785 }
786 this->FileName = "";
787 return true;
788}
578bfd0a 789 /*}}}*/
8e06abb2 790// FileFd::~File - Closes the file /*{{{*/
578bfd0a
AL
791// ---------------------------------------------------------------------
792/* If the proper modes are selected then we close the Fd and possibly
793 unlink the file on error. */
8e06abb2 794FileFd::~FileFd()
578bfd0a
AL
795{
796 Close();
797}
798 /*}}}*/
8e06abb2 799// FileFd::Read - Read a bit of the file /*{{{*/
578bfd0a 800// ---------------------------------------------------------------------
b0db36b1
AL
801/* We are carefull to handle interruption by a signal while reading
802 gracefully. */
650faab0 803bool FileFd::Read(void *To,unsigned long long Size,unsigned long long *Actual)
578bfd0a 804{
b0db36b1
AL
805 int Res;
806 errno = 0;
f604cf55
AL
807 if (Actual != 0)
808 *Actual = 0;
809
b0db36b1 810 do
578bfd0a 811 {
a3a03f5d 812 if (gz != NULL)
813 Res = gzread(gz,To,Size);
814 else
815 Res = read(iFd,To,Size);
b0db36b1
AL
816 if (Res < 0 && errno == EINTR)
817 continue;
818 if (Res < 0)
819 {
820 Flags |= Fail;
b2e465d6 821 return _error->Errno("read",_("Read error"));
b0db36b1 822 }
578bfd0a 823
b0db36b1
AL
824 To = (char *)To + Res;
825 Size -= Res;
f604cf55
AL
826 if (Actual != 0)
827 *Actual += Res;
b0db36b1
AL
828 }
829 while (Res > 0 && Size > 0);
830
831 if (Size == 0)
832 return true;
833
ddc1d8d0 834 // Eof handling
f604cf55 835 if (Actual != 0)
ddc1d8d0
AL
836 {
837 Flags |= HitEof;
838 return true;
839 }
840
b0db36b1 841 Flags |= Fail;
650faab0 842 return _error->Error(_("read, still have %llu to read but none left"), Size);
578bfd0a
AL
843}
844 /*}}}*/
8e06abb2 845// FileFd::Write - Write to the file /*{{{*/
578bfd0a
AL
846// ---------------------------------------------------------------------
847/* */
650faab0 848bool FileFd::Write(const void *From,unsigned long long Size)
578bfd0a 849{
b0db36b1
AL
850 int Res;
851 errno = 0;
852 do
578bfd0a 853 {
a3a03f5d 854 if (gz != NULL)
855 Res = gzwrite(gz,From,Size);
856 else
857 Res = write(iFd,From,Size);
b0db36b1
AL
858 if (Res < 0 && errno == EINTR)
859 continue;
860 if (Res < 0)
861 {
862 Flags |= Fail;
b2e465d6 863 return _error->Errno("write",_("Write error"));
b0db36b1
AL
864 }
865
866 From = (char *)From + Res;
867 Size -= Res;
578bfd0a 868 }
b0db36b1 869 while (Res > 0 && Size > 0);
578bfd0a 870
b0db36b1
AL
871 if (Size == 0)
872 return true;
873
874 Flags |= Fail;
650faab0 875 return _error->Error(_("write, still have %llu to write but couldn't"), Size);
578bfd0a
AL
876}
877 /*}}}*/
8e06abb2 878// FileFd::Seek - Seek in the file /*{{{*/
578bfd0a
AL
879// ---------------------------------------------------------------------
880/* */
650faab0 881bool FileFd::Seek(unsigned long long To)
578bfd0a 882{
a3a03f5d 883 int res;
884 if (gz)
885 res = gzseek(gz,To,SEEK_SET);
886 else
887 res = lseek(iFd,To,SEEK_SET);
888 if (res != (signed)To)
578bfd0a
AL
889 {
890 Flags |= Fail;
650faab0 891 return _error->Error("Unable to seek to %llu", To);
578bfd0a
AL
892 }
893
727f18af
AL
894 return true;
895}
896 /*}}}*/
897// FileFd::Skip - Seek in the file /*{{{*/
898// ---------------------------------------------------------------------
899/* */
650faab0 900bool FileFd::Skip(unsigned long long Over)
727f18af 901{
a3a03f5d 902 int res;
903 if (gz)
904 res = gzseek(gz,Over,SEEK_CUR);
905 else
906 res = lseek(iFd,Over,SEEK_CUR);
907 if (res < 0)
727f18af
AL
908 {
909 Flags |= Fail;
650faab0 910 return _error->Error("Unable to seek ahead %llu",Over);
727f18af
AL
911 }
912
6d5dd02a
AL
913 return true;
914}
915 /*}}}*/
916// FileFd::Truncate - Truncate the file /*{{{*/
917// ---------------------------------------------------------------------
918/* */
650faab0 919bool FileFd::Truncate(unsigned long long To)
6d5dd02a 920{
a3a03f5d 921 if (gz)
922 {
923 Flags |= Fail;
924 return _error->Error("Truncating gzipped files is not implemented (%s)", FileName.c_str());
925 }
6d5dd02a
AL
926 if (ftruncate(iFd,To) != 0)
927 {
928 Flags |= Fail;
650faab0 929 return _error->Error("Unable to truncate to %llu",To);
6d5dd02a
AL
930 }
931
578bfd0a
AL
932 return true;
933}
934 /*}}}*/
7f25bdff
AL
935// FileFd::Tell - Current seek position /*{{{*/
936// ---------------------------------------------------------------------
937/* */
650faab0 938unsigned long long FileFd::Tell()
7f25bdff 939{
a3a03f5d 940 off_t Res;
941 if (gz)
942 Res = gztell(gz);
943 else
944 Res = lseek(iFd,0,SEEK_CUR);
7f25bdff
AL
945 if (Res == (off_t)-1)
946 _error->Errno("lseek","Failed to determine the current file position");
947 return Res;
948}
949 /*}}}*/
4260fd39 950// FileFd::FileSize - Return the size of the file /*{{{*/
578bfd0a
AL
951// ---------------------------------------------------------------------
952/* */
650faab0 953unsigned long long FileFd::FileSize()
578bfd0a
AL
954{
955 struct stat Buf;
9c182afa 956
44dc669e
MV
957 if (fstat(iFd,&Buf) != 0)
958 return _error->Errno("fstat","Unable to determine the file size");
4260fd39
DK
959 return Buf.st_size;
960}
961 /*}}}*/
962// FileFd::Size - Return the size of the content in the file /*{{{*/
963// ---------------------------------------------------------------------
964/* */
650faab0 965unsigned long long FileFd::Size()
4260fd39 966{
650faab0 967 unsigned long long size = FileSize();
44dc669e
MV
968
969 // only check gzsize if we are actually a gzip file, just checking for
970 // "gz" is not sufficient as uncompressed files will be opened with
971 // gzopen in "direct" mode as well
972 if (gz && !gzdirect(gz) && size > 0)
9c182afa
MP
973 {
974 /* unfortunately zlib.h doesn't provide a gzsize(), so we have to do
975 * this ourselves; the original (uncompressed) file size is the last 32
976 * bits of the file */
650faab0 977 // FIXME: Size for gz-files is limited by 32bit… no largefile support
4260fd39 978 off_t orig_pos = lseek(iFd, 0, SEEK_CUR);
9c182afa
MP
979 if (lseek(iFd, -4, SEEK_END) < 0)
980 return _error->Errno("lseek","Unable to seek to end of gzipped file");
f330c0f3 981 size = 0L;
9c182afa
MP
982 if (read(iFd, &size, 4) != 4)
983 return _error->Errno("read","Unable to read original size of gzipped file");
f330c0f3
DK
984
985#ifdef WORDS_BIGENDIAN
2cae0ccb
DK
986 uint32_t tmp_size = size;
987 uint8_t const * const p = (uint8_t const * const) &tmp_size;
988 tmp_size = (p[3] << 24) | (p[2] << 16) | (p[1] << 8) | p[0];
989 size = tmp_size;
f330c0f3 990#endif
9c182afa
MP
991
992 if (lseek(iFd, orig_pos, SEEK_SET) < 0)
993 return _error->Errno("lseek","Unable to seek in gzipped file");
994 return size;
995 }
996
44dc669e 997 return size;
578bfd0a
AL
998}
999 /*}}}*/
8e06abb2 1000// FileFd::Close - Close the file if the close flag is set /*{{{*/
578bfd0a
AL
1001// ---------------------------------------------------------------------
1002/* */
8e06abb2 1003bool FileFd::Close()
578bfd0a
AL
1004{
1005 bool Res = true;
1006 if ((Flags & AutoClose) == AutoClose)
d13c2d3f 1007 {
1008 if (gz != NULL) {
3184b4cf 1009 int const e = gzclose(gz);
d13c2d3f 1010 // gzdopen() on empty files always fails with "buffer error" here, ignore that
1011 if (e != 0 && e != Z_BUF_ERROR)
3184b4cf 1012 Res &= _error->Errno("close",_("Problem closing the gzip file %s"), FileName.c_str());
d13c2d3f 1013 } else
1014 if (iFd > 0 && close(iFd) != 0)
3184b4cf 1015 Res &= _error->Errno("close",_("Problem closing the file %s"), FileName.c_str());
d13c2d3f 1016 }
3010fb0e 1017
62d073d9 1018 if ((Flags & Replace) == Replace && iFd >= 0) {
3010fb0e 1019 if (rename(TemporaryFileName.c_str(), FileName.c_str()) != 0)
62d073d9
DK
1020 Res &= _error->Errno("rename",_("Problem renaming the file %s to %s"), TemporaryFileName.c_str(), FileName.c_str());
1021
fd3b761e 1022 FileName = TemporaryFileName; // for the unlink() below.
3010fb0e 1023 }
62d073d9
DK
1024
1025 iFd = -1;
a3a03f5d 1026 gz = NULL;
62d073d9 1027
578bfd0a
AL
1028 if ((Flags & Fail) == Fail && (Flags & DelOnFail) == DelOnFail &&
1029 FileName.empty() == false)
1030 if (unlink(FileName.c_str()) != 0)
62d073d9 1031 Res &= _error->WarningE("unlnk",_("Problem unlinking the file %s"), FileName.c_str());
3010fb0e
JAK
1032
1033
578bfd0a
AL
1034 return Res;
1035}
1036 /*}}}*/
b2e465d6
AL
1037// FileFd::Sync - Sync the file /*{{{*/
1038// ---------------------------------------------------------------------
1039/* */
1040bool FileFd::Sync()
1041{
1042#ifdef _POSIX_SYNCHRONIZED_IO
1043 if (fsync(iFd) != 0)
1044 return _error->Errno("sync",_("Problem syncing the file"));
1045#endif
1046 return true;
1047}
1048 /*}}}*/