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