]> git.saurik.com Git - apt.git/blame - apt-pkg/contrib/fileutl.cc
test-indexes: Use /etc/apt from temporary work dir, not from system
[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);
a3a03f5d 607 if (iFd > 0 && FileName.compare(FileName.size()-3, 3, ".gz") == 0) {
608 gz = gzdopen (iFd, "r");
609 if (gz == NULL) {
610 close (iFd);
611 iFd = -1;
612 }
613 }
578bfd0a
AL
614 break;
615
616 case WriteEmpty:
50b513a1
AL
617 {
618 struct stat Buf;
459681d3 619 if (lstat(FileName.c_str(),&Buf) == 0 && S_ISLNK(Buf.st_mode))
50b513a1
AL
620 unlink(FileName.c_str());
621 iFd = open(FileName.c_str(),O_RDWR | O_CREAT | O_TRUNC,Perms);
622 break;
623 }
578bfd0a
AL
624
625 case WriteExists:
626 iFd = open(FileName.c_str(),O_RDWR);
627 break;
0a8e3465
AL
628
629 case WriteAny:
630 iFd = open(FileName.c_str(),O_RDWR | O_CREAT,Perms);
d38b7b3d 631 break;
f08fcf34
AL
632
633 case WriteTemp:
4decd43c
AL
634 unlink(FileName.c_str());
635 iFd = open(FileName.c_str(),O_RDWR | O_CREAT | O_EXCL,Perms);
f08fcf34 636 break;
578bfd0a
AL
637 }
638
639 if (iFd < 0)
b2e465d6 640 return _error->Errno("open",_("Could not open file %s"),FileName.c_str());
13d87e2e
AL
641
642 this->FileName = FileName;
643 SetCloseExec(iFd,true);
644 return true;
578bfd0a
AL
645}
646 /*}}}*/
8e06abb2 647// FileFd::~File - Closes the file /*{{{*/
578bfd0a
AL
648// ---------------------------------------------------------------------
649/* If the proper modes are selected then we close the Fd and possibly
650 unlink the file on error. */
8e06abb2 651FileFd::~FileFd()
578bfd0a
AL
652{
653 Close();
654}
655 /*}}}*/
8e06abb2 656// FileFd::Read - Read a bit of the file /*{{{*/
578bfd0a 657// ---------------------------------------------------------------------
b0db36b1
AL
658/* We are carefull to handle interruption by a signal while reading
659 gracefully. */
f604cf55 660bool FileFd::Read(void *To,unsigned long Size,unsigned long *Actual)
578bfd0a 661{
b0db36b1
AL
662 int Res;
663 errno = 0;
f604cf55
AL
664 if (Actual != 0)
665 *Actual = 0;
666
b0db36b1 667 do
578bfd0a 668 {
a3a03f5d 669 if (gz != NULL)
670 Res = gzread(gz,To,Size);
671 else
672 Res = read(iFd,To,Size);
b0db36b1
AL
673 if (Res < 0 && errno == EINTR)
674 continue;
675 if (Res < 0)
676 {
677 Flags |= Fail;
b2e465d6 678 return _error->Errno("read",_("Read error"));
b0db36b1 679 }
578bfd0a 680
b0db36b1
AL
681 To = (char *)To + Res;
682 Size -= Res;
f604cf55
AL
683 if (Actual != 0)
684 *Actual += Res;
b0db36b1
AL
685 }
686 while (Res > 0 && Size > 0);
687
688 if (Size == 0)
689 return true;
690
ddc1d8d0 691 // Eof handling
f604cf55 692 if (Actual != 0)
ddc1d8d0
AL
693 {
694 Flags |= HitEof;
695 return true;
696 }
697
b0db36b1 698 Flags |= Fail;
b2e465d6 699 return _error->Error(_("read, still have %lu to read but none left"),Size);
578bfd0a
AL
700}
701 /*}}}*/
8e06abb2 702// FileFd::Write - Write to the file /*{{{*/
578bfd0a
AL
703// ---------------------------------------------------------------------
704/* */
a05599f1 705bool FileFd::Write(const void *From,unsigned long Size)
578bfd0a 706{
b0db36b1
AL
707 int Res;
708 errno = 0;
709 do
578bfd0a 710 {
a3a03f5d 711 if (gz != NULL)
712 Res = gzwrite(gz,From,Size);
713 else
714 Res = write(iFd,From,Size);
b0db36b1
AL
715 if (Res < 0 && errno == EINTR)
716 continue;
717 if (Res < 0)
718 {
719 Flags |= Fail;
b2e465d6 720 return _error->Errno("write",_("Write error"));
b0db36b1
AL
721 }
722
723 From = (char *)From + Res;
724 Size -= Res;
578bfd0a 725 }
b0db36b1 726 while (Res > 0 && Size > 0);
578bfd0a 727
b0db36b1
AL
728 if (Size == 0)
729 return true;
730
731 Flags |= Fail;
b2e465d6 732 return _error->Error(_("write, still have %lu to write but couldn't"),Size);
578bfd0a
AL
733}
734 /*}}}*/
8e06abb2 735// FileFd::Seek - Seek in the file /*{{{*/
578bfd0a
AL
736// ---------------------------------------------------------------------
737/* */
8e06abb2 738bool FileFd::Seek(unsigned long To)
578bfd0a 739{
a3a03f5d 740 int res;
741 if (gz)
742 res = gzseek(gz,To,SEEK_SET);
743 else
744 res = lseek(iFd,To,SEEK_SET);
745 if (res != (signed)To)
578bfd0a
AL
746 {
747 Flags |= Fail;
b2e465d6 748 return _error->Error("Unable to seek to %lu",To);
578bfd0a
AL
749 }
750
727f18af
AL
751 return true;
752}
753 /*}}}*/
754// FileFd::Skip - Seek in the file /*{{{*/
755// ---------------------------------------------------------------------
756/* */
757bool FileFd::Skip(unsigned long Over)
758{
a3a03f5d 759 int res;
760 if (gz)
761 res = gzseek(gz,Over,SEEK_CUR);
762 else
763 res = lseek(iFd,Over,SEEK_CUR);
764 if (res < 0)
727f18af
AL
765 {
766 Flags |= Fail;
b2e465d6 767 return _error->Error("Unable to seek ahead %lu",Over);
727f18af
AL
768 }
769
6d5dd02a
AL
770 return true;
771}
772 /*}}}*/
773// FileFd::Truncate - Truncate the file /*{{{*/
774// ---------------------------------------------------------------------
775/* */
776bool FileFd::Truncate(unsigned long To)
777{
a3a03f5d 778 if (gz)
779 {
780 Flags |= Fail;
781 return _error->Error("Truncating gzipped files is not implemented (%s)", FileName.c_str());
782 }
6d5dd02a
AL
783 if (ftruncate(iFd,To) != 0)
784 {
785 Flags |= Fail;
b2e465d6 786 return _error->Error("Unable to truncate to %lu",To);
6d5dd02a
AL
787 }
788
578bfd0a
AL
789 return true;
790}
791 /*}}}*/
7f25bdff
AL
792// FileFd::Tell - Current seek position /*{{{*/
793// ---------------------------------------------------------------------
794/* */
795unsigned long FileFd::Tell()
796{
a3a03f5d 797 off_t Res;
798 if (gz)
799 Res = gztell(gz);
800 else
801 Res = lseek(iFd,0,SEEK_CUR);
7f25bdff
AL
802 if (Res == (off_t)-1)
803 _error->Errno("lseek","Failed to determine the current file position");
804 return Res;
805}
806 /*}}}*/
8e06abb2 807// FileFd::Size - Return the size of the file /*{{{*/
578bfd0a
AL
808// ---------------------------------------------------------------------
809/* */
8e06abb2 810unsigned long FileFd::Size()
578bfd0a 811{
a3a03f5d 812 //TODO: For gz, do we need the actual file size here or the uncompressed length?
578bfd0a
AL
813 struct stat Buf;
814 if (fstat(iFd,&Buf) != 0)
815 return _error->Errno("fstat","Unable to determine the file size");
816 return Buf.st_size;
817}
818 /*}}}*/
8e06abb2 819// FileFd::Close - Close the file if the close flag is set /*{{{*/
578bfd0a
AL
820// ---------------------------------------------------------------------
821/* */
8e06abb2 822bool FileFd::Close()
578bfd0a
AL
823{
824 bool Res = true;
825 if ((Flags & AutoClose) == AutoClose)
a3a03f5d 826 if ((gz != NULL && gzclose(gz) != 0) || (gz == NULL && iFd > 0 && close(iFd) != 0))
b2e465d6 827 Res &= _error->Errno("close",_("Problem closing the file"));
1164783d 828 iFd = -1;
a3a03f5d 829 gz = NULL;
1164783d 830
578bfd0a
AL
831 if ((Flags & Fail) == Fail && (Flags & DelOnFail) == DelOnFail &&
832 FileName.empty() == false)
833 if (unlink(FileName.c_str()) != 0)
b2e465d6 834 Res &= _error->WarningE("unlnk",_("Problem unlinking the file"));
578bfd0a
AL
835 return Res;
836}
837 /*}}}*/
b2e465d6
AL
838// FileFd::Sync - Sync the file /*{{{*/
839// ---------------------------------------------------------------------
840/* */
841bool FileFd::Sync()
842{
843#ifdef _POSIX_SYNCHRONIZED_IO
844 if (fsync(iFd) != 0)
845 return _error->Errno("sync",_("Problem syncing the file"));
846#endif
847 return true;
848}
849 /*}}}*/