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