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