]> git.saurik.com Git - apt.git/blame - apt-pkg/contrib/fileutl.cc
- backport forgotten Valid-Until patch from the obsolete experimental
[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>.
578bfd0a 14
614adaa0
MV
15 The exception is RunScripts() it is under the GPLv2
16
578bfd0a
AL
17 ##################################################################### */
18 /*}}}*/
19// Include Files /*{{{*/
094a497d 20#include <apt-pkg/fileutl.h>
1cd1c398 21#include <apt-pkg/strutl.h>
094a497d 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 /*}}}*/
1cd1c398
DK
201// DirectoryExists - Check if a directory exists and is really one /*{{{*/
202// ---------------------------------------------------------------------
203/* */
204bool DirectoryExists(string const &Path)
205{
206 struct stat Buf;
207 if (stat(Path.c_str(),&Buf) != 0)
208 return false;
209 return ((Buf.st_mode & S_IFDIR) != 0);
210}
211 /*}}}*/
212// CreateDirectory - poor man's mkdir -p guarded by a parent directory /*{{{*/
213// ---------------------------------------------------------------------
214/* This method will create all directories needed for path in good old
215 mkdir -p style but refuses to do this if Parent is not a prefix of
216 this Path. Example: /var/cache/ and /var/cache/apt/archives are given,
217 so it will create apt/archives if /var/cache exists - on the other
218 hand if the parent is /var/lib the creation will fail as this path
219 is not a parent of the path to be generated. */
220bool CreateDirectory(string const &Parent, string const &Path)
221{
222 if (Parent.empty() == true || Path.empty() == true)
223 return false;
224
225 if (DirectoryExists(Path) == true)
226 return true;
227
228 if (DirectoryExists(Parent) == false)
229 return false;
230
231 // we are not going to create directories "into the blue"
232 if (Path.find(Parent, 0) != 0)
233 return false;
234
235 vector<string> const dirs = VectorizeString(Path.substr(Parent.size()), '/');
236 string progress = Parent;
237 for (vector<string>::const_iterator d = dirs.begin(); d != dirs.end(); ++d)
238 {
239 if (d->empty() == true)
240 continue;
241
242 progress.append("/").append(*d);
243 if (DirectoryExists(progress) == true)
244 continue;
245
246 if (mkdir(progress.c_str(), 0755) != 0)
247 return false;
248 }
249 return true;
250}
251 /*}}}*/
46e39c8e
MV
252// GetListOfFilesInDir - returns a vector of files in the given dir /*{{{*/
253// ---------------------------------------------------------------------
254/* If an extension is given only files with this extension are included
255 in the returned vector, otherwise every "normal" file is included. */
b39c1859
MV
256std::vector<string> GetListOfFilesInDir(string const &Dir, string const &Ext,
257 bool const &SortList, bool const &AllowNoExt)
258{
259 std::vector<string> ext;
260 ext.reserve(2);
261 if (Ext.empty() == false)
262 ext.push_back(Ext);
263 if (AllowNoExt == true && ext.empty() == false)
264 ext.push_back("");
265 return GetListOfFilesInDir(Dir, ext, SortList);
266}
267std::vector<string> GetListOfFilesInDir(string const &Dir, std::vector<string> const &Ext,
268 bool const &SortList)
269{
270 // Attention debuggers: need to be set with the environment config file!
271 bool const Debug = _config->FindB("Debug::GetListOfFilesInDir", false);
272 if (Debug == true)
273 {
274 std::clog << "Accept in " << Dir << " only files with the following " << Ext.size() << " extensions:" << std::endl;
275 if (Ext.empty() == true)
276 std::clog << "\tNO extension" << std::endl;
277 else
278 for (std::vector<string>::const_iterator e = Ext.begin();
279 e != Ext.end(); ++e)
280 std::clog << '\t' << (e->empty() == true ? "NO" : *e) << " extension" << std::endl;
281 }
282
46e39c8e
MV
283 std::vector<string> List;
284 DIR *D = opendir(Dir.c_str());
285 if (D == 0)
286 {
287 _error->Errno("opendir",_("Unable to read %s"),Dir.c_str());
288 return List;
289 }
290
291 for (struct dirent *Ent = readdir(D); Ent != 0; Ent = readdir(D))
292 {
b39c1859 293 // skip "hidden" files
46e39c8e
MV
294 if (Ent->d_name[0] == '.')
295 continue;
296
b39c1859
MV
297 // check for accepted extension:
298 // no extension given -> periods are bad as hell!
299 // extensions given -> "" extension allows no extension
300 if (Ext.empty() == false)
301 {
302 string d_ext = flExtension(Ent->d_name);
303 if (d_ext == Ent->d_name) // no extension
304 {
305 if (std::find(Ext.begin(), Ext.end(), "") == Ext.end())
306 {
307 if (Debug == true)
308 std::clog << "Bad file: " << Ent->d_name << " → no extension" << std::endl;
309 continue;
310 }
311 }
312 else if (std::find(Ext.begin(), Ext.end(), d_ext) == Ext.end())
313 {
314 if (Debug == true)
315 std::clog << "Bad file: " << Ent->d_name << " → bad extension »" << flExtension(Ent->d_name) << "«" << std::endl;
316 continue;
317 }
318 }
46e39c8e 319
b39c1859 320 // Skip bad filenames ala run-parts
46e39c8e
MV
321 const char *C = Ent->d_name;
322 for (; *C != 0; ++C)
323 if (isalpha(*C) == 0 && isdigit(*C) == 0
b39c1859
MV
324 && *C != '_' && *C != '-') {
325 // no required extension -> dot is a bad character
326 if (*C == '.' && Ext.empty() == false)
327 continue;
46e39c8e 328 break;
b39c1859 329 }
46e39c8e 330
b39c1859 331 // we don't reach the end of the name -> bad character included
46e39c8e 332 if (*C != 0)
b39c1859
MV
333 {
334 if (Debug == true)
335 std::clog << "Bad file: " << Ent->d_name << " → bad character »"
336 << *C << "« in filename (period allowed: " << (Ext.empty() ? "no" : "yes") << ")" << std::endl;
337 continue;
338 }
339
340 // skip filenames which end with a period. These are never valid
341 if (*(C - 1) == '.')
342 {
343 if (Debug == true)
344 std::clog << "Bad file: " << Ent->d_name << " → Period as last character" << std::endl;
46e39c8e 345 continue;
b39c1859 346 }
46e39c8e
MV
347
348 // Make sure it is a file and not something else
349 string const File = flCombine(Dir,Ent->d_name);
350 struct stat St;
351 if (stat(File.c_str(),&St) != 0 || S_ISREG(St.st_mode) == 0)
b39c1859
MV
352 {
353 if (Debug == true)
354 std::clog << "Bad file: " << Ent->d_name << " → stat says not a good file" << std::endl;
46e39c8e 355 continue;
b39c1859 356 }
46e39c8e 357
b39c1859
MV
358 if (Debug == true)
359 std::clog << "Accept file: " << Ent->d_name << " in " << Dir << std::endl;
46e39c8e
MV
360 List.push_back(File);
361 }
362 closedir(D);
363
364 if (SortList == true)
365 std::sort(List.begin(),List.end());
366 return List;
367}
368 /*}}}*/
578bfd0a
AL
369// SafeGetCWD - This is a safer getcwd that returns a dynamic string /*{{{*/
370// ---------------------------------------------------------------------
371/* We return / on failure. */
372string SafeGetCWD()
373{
374 // Stash the current dir.
375 char S[300];
376 S[0] = 0;
7f25bdff 377 if (getcwd(S,sizeof(S)-2) == 0)
578bfd0a 378 return "/";
7f25bdff
AL
379 unsigned int Len = strlen(S);
380 S[Len] = '/';
381 S[Len+1] = 0;
578bfd0a
AL
382 return S;
383}
384 /*}}}*/
8ce4327b
AL
385// flNotDir - Strip the directory from the filename /*{{{*/
386// ---------------------------------------------------------------------
387/* */
388string flNotDir(string File)
389{
390 string::size_type Res = File.rfind('/');
391 if (Res == string::npos)
392 return File;
393 Res++;
394 return string(File,Res,Res - File.length());
395}
396 /*}}}*/
d38b7b3d
AL
397// flNotFile - Strip the file from the directory name /*{{{*/
398// ---------------------------------------------------------------------
171c45bc 399/* Result ends in a / */
d38b7b3d
AL
400string flNotFile(string File)
401{
402 string::size_type Res = File.rfind('/');
403 if (Res == string::npos)
171c45bc 404 return "./";
d38b7b3d
AL
405 Res++;
406 return string(File,0,Res);
407}
408 /*}}}*/
b2e465d6
AL
409// flExtension - Return the extension for the file /*{{{*/
410// ---------------------------------------------------------------------
411/* */
412string flExtension(string File)
413{
414 string::size_type Res = File.rfind('.');
415 if (Res == string::npos)
416 return File;
417 Res++;
418 return string(File,Res,Res - File.length());
419}
420 /*}}}*/
421c8d10
AL
421// flNoLink - If file is a symlink then deref it /*{{{*/
422// ---------------------------------------------------------------------
423/* If the name is not a link then the returned path is the input. */
424string flNoLink(string File)
425{
426 struct stat St;
427 if (lstat(File.c_str(),&St) != 0 || S_ISLNK(St.st_mode) == 0)
428 return File;
429 if (stat(File.c_str(),&St) != 0)
430 return File;
431
432 /* Loop resolving the link. There is no need to limit the number of
433 loops because the stat call above ensures that the symlink is not
434 circular */
435 char Buffer[1024];
436 string NFile = File;
437 while (1)
438 {
439 // Read the link
440 int Res;
441 if ((Res = readlink(NFile.c_str(),Buffer,sizeof(Buffer))) <= 0 ||
442 (unsigned)Res >= sizeof(Buffer))
443 return File;
444
445 // Append or replace the previous path
446 Buffer[Res] = 0;
447 if (Buffer[0] == '/')
448 NFile = Buffer;
449 else
450 NFile = flNotFile(NFile) + Buffer;
451
452 // See if we are done
453 if (lstat(NFile.c_str(),&St) != 0)
454 return File;
455 if (S_ISLNK(St.st_mode) == 0)
456 return NFile;
457 }
458}
459 /*}}}*/
b2e465d6
AL
460// flCombine - Combine a file and a directory /*{{{*/
461// ---------------------------------------------------------------------
462/* If the file is an absolute path then it is just returned, otherwise
463 the directory is pre-pended to it. */
464string flCombine(string Dir,string File)
465{
466 if (File.empty() == true)
467 return string();
468
469 if (File[0] == '/' || Dir.empty() == true)
470 return File;
471 if (File.length() >= 2 && File[0] == '.' && File[1] == '/')
472 return File;
473 if (Dir[Dir.length()-1] == '/')
474 return Dir + File;
475 return Dir + '/' + File;
476}
477 /*}}}*/
3b5421b4
AL
478// SetCloseExec - Set the close on exec flag /*{{{*/
479// ---------------------------------------------------------------------
480/* */
481void SetCloseExec(int Fd,bool Close)
482{
483 if (fcntl(Fd,F_SETFD,(Close == false)?0:FD_CLOEXEC) != 0)
484 {
485 cerr << "FATAL -> Could not set close on exec " << strerror(errno) << endl;
486 exit(100);
487 }
488}
489 /*}}}*/
490// SetNonBlock - Set the nonblocking flag /*{{{*/
491// ---------------------------------------------------------------------
492/* */
493void SetNonBlock(int Fd,bool Block)
494{
0a8a80e5
AL
495 int Flags = fcntl(Fd,F_GETFL) & (~O_NONBLOCK);
496 if (fcntl(Fd,F_SETFL,Flags | ((Block == false)?0:O_NONBLOCK)) != 0)
3b5421b4
AL
497 {
498 cerr << "FATAL -> Could not set non-blocking flag " << strerror(errno) << endl;
499 exit(100);
500 }
501}
502 /*}}}*/
503// WaitFd - Wait for a FD to become readable /*{{{*/
504// ---------------------------------------------------------------------
b2e465d6 505/* This waits for a FD to become readable using select. It is useful for
6d5dd02a
AL
506 applications making use of non-blocking sockets. The timeout is
507 in seconds. */
1084d58a 508bool WaitFd(int Fd,bool write,unsigned long timeout)
3b5421b4
AL
509{
510 fd_set Set;
cc2313b7 511 struct timeval tv;
3b5421b4
AL
512 FD_ZERO(&Set);
513 FD_SET(Fd,&Set);
6d5dd02a
AL
514 tv.tv_sec = timeout;
515 tv.tv_usec = 0;
1084d58a 516 if (write == true)
b0db36b1
AL
517 {
518 int Res;
519 do
520 {
521 Res = select(Fd+1,0,&Set,0,(timeout != 0?&tv:0));
522 }
523 while (Res < 0 && errno == EINTR);
524
525 if (Res <= 0)
526 return false;
1084d58a
AL
527 }
528 else
529 {
b0db36b1
AL
530 int Res;
531 do
532 {
533 Res = select(Fd+1,&Set,0,0,(timeout != 0?&tv:0));
534 }
535 while (Res < 0 && errno == EINTR);
536
537 if (Res <= 0)
538 return false;
cc2313b7 539 }
1084d58a 540
3b5421b4
AL
541 return true;
542}
543 /*}}}*/
54676e1a
AL
544// ExecFork - Magical fork that sanitizes the context before execing /*{{{*/
545// ---------------------------------------------------------------------
546/* This is used if you want to cleanse the environment for the forked
547 child, it fixes up the important signals and nukes all of the fds,
548 otherwise acts like normal fork. */
75ef8f14 549pid_t ExecFork()
54676e1a
AL
550{
551 // Fork off the process
552 pid_t Process = fork();
553 if (Process < 0)
554 {
555 cerr << "FATAL -> Failed to fork." << endl;
556 exit(100);
557 }
558
559 // Spawn the subprocess
560 if (Process == 0)
561 {
562 // Setup the signals
563 signal(SIGPIPE,SIG_DFL);
564 signal(SIGQUIT,SIG_DFL);
565 signal(SIGINT,SIG_DFL);
566 signal(SIGWINCH,SIG_DFL);
567 signal(SIGCONT,SIG_DFL);
568 signal(SIGTSTP,SIG_DFL);
75ef8f14
MV
569
570 set<int> KeepFDs;
571 Configuration::Item const *Opts = _config->Tree("APT::Keep-Fds");
572 if (Opts != 0 && Opts->Child != 0)
573 {
574 Opts = Opts->Child;
575 for (; Opts != 0; Opts = Opts->Next)
576 {
577 if (Opts->Value.empty() == true)
578 continue;
579 int fd = atoi(Opts->Value.c_str());
580 KeepFDs.insert(fd);
581 }
582 }
583
54676e1a
AL
584 // Close all of our FDs - just in case
585 for (int K = 3; K != 40; K++)
75ef8f14
MV
586 {
587 if(KeepFDs.find(K) == KeepFDs.end())
007dc9e0 588 fcntl(K,F_SETFD,FD_CLOEXEC);
75ef8f14 589 }
54676e1a
AL
590 }
591
592 return Process;
593}
594 /*}}}*/
ddc1d8d0
AL
595// ExecWait - Fancy waitpid /*{{{*/
596// ---------------------------------------------------------------------
2c9a72d1 597/* Waits for the given sub process. If Reap is set then no errors are
ddc1d8d0
AL
598 generated. Otherwise a failed subprocess will generate a proper descriptive
599 message */
3826564e 600bool ExecWait(pid_t Pid,const char *Name,bool Reap)
ddc1d8d0
AL
601{
602 if (Pid <= 1)
603 return true;
604
605 // Wait and collect the error code
606 int Status;
607 while (waitpid(Pid,&Status,0) != Pid)
608 {
609 if (errno == EINTR)
610 continue;
611
612 if (Reap == true)
613 return false;
614
db0db9fe 615 return _error->Error(_("Waited for %s but it wasn't there"),Name);
ddc1d8d0
AL
616 }
617
618
619 // Check for an error code.
620 if (WIFEXITED(Status) == 0 || WEXITSTATUS(Status) != 0)
621 {
622 if (Reap == true)
623 return false;
ab7f4d7c 624 if (WIFSIGNALED(Status) != 0)
40e7fe0e 625 {
ab7f4d7c
MV
626 if( WTERMSIG(Status) == SIGSEGV)
627 return _error->Error(_("Sub-process %s received a segmentation fault."),Name);
628 else
629 return _error->Error(_("Sub-process %s received signal %u."),Name, WTERMSIG(Status));
40e7fe0e 630 }
ddc1d8d0
AL
631
632 if (WIFEXITED(Status) != 0)
b2e465d6 633 return _error->Error(_("Sub-process %s returned an error code (%u)"),Name,WEXITSTATUS(Status));
ddc1d8d0 634
b2e465d6 635 return _error->Error(_("Sub-process %s exited unexpectedly"),Name);
ddc1d8d0
AL
636 }
637
638 return true;
639}
640 /*}}}*/
578bfd0a 641
13d87e2e 642// FileFd::Open - Open a file /*{{{*/
578bfd0a
AL
643// ---------------------------------------------------------------------
644/* The most commonly used open mode combinations are given with Mode */
13d87e2e 645bool FileFd::Open(string FileName,OpenMode Mode, unsigned long Perms)
578bfd0a 646{
13d87e2e 647 Close();
1164783d 648 Flags = AutoClose;
578bfd0a
AL
649 switch (Mode)
650 {
651 case ReadOnly:
652 iFd = open(FileName.c_str(),O_RDONLY);
653 break;
654
655 case WriteEmpty:
50b513a1
AL
656 {
657 struct stat Buf;
459681d3 658 if (lstat(FileName.c_str(),&Buf) == 0 && S_ISLNK(Buf.st_mode))
50b513a1
AL
659 unlink(FileName.c_str());
660 iFd = open(FileName.c_str(),O_RDWR | O_CREAT | O_TRUNC,Perms);
661 break;
662 }
578bfd0a
AL
663
664 case WriteExists:
665 iFd = open(FileName.c_str(),O_RDWR);
666 break;
0a8e3465
AL
667
668 case WriteAny:
669 iFd = open(FileName.c_str(),O_RDWR | O_CREAT,Perms);
d38b7b3d 670 break;
f08fcf34
AL
671
672 case WriteTemp:
4decd43c
AL
673 unlink(FileName.c_str());
674 iFd = open(FileName.c_str(),O_RDWR | O_CREAT | O_EXCL,Perms);
f08fcf34 675 break;
578bfd0a
AL
676 }
677
678 if (iFd < 0)
b2e465d6 679 return _error->Errno("open",_("Could not open file %s"),FileName.c_str());
13d87e2e
AL
680
681 this->FileName = FileName;
682 SetCloseExec(iFd,true);
683 return true;
578bfd0a
AL
684}
685 /*}}}*/
8e06abb2 686// FileFd::~File - Closes the file /*{{{*/
578bfd0a
AL
687// ---------------------------------------------------------------------
688/* If the proper modes are selected then we close the Fd and possibly
689 unlink the file on error. */
8e06abb2 690FileFd::~FileFd()
578bfd0a
AL
691{
692 Close();
693}
694 /*}}}*/
8e06abb2 695// FileFd::Read - Read a bit of the file /*{{{*/
578bfd0a 696// ---------------------------------------------------------------------
b0db36b1
AL
697/* We are carefull to handle interruption by a signal while reading
698 gracefully. */
f604cf55 699bool FileFd::Read(void *To,unsigned long Size,unsigned long *Actual)
578bfd0a 700{
b0db36b1
AL
701 int Res;
702 errno = 0;
f604cf55
AL
703 if (Actual != 0)
704 *Actual = 0;
705
b0db36b1 706 do
578bfd0a 707 {
b0db36b1
AL
708 Res = read(iFd,To,Size);
709 if (Res < 0 && errno == EINTR)
710 continue;
711 if (Res < 0)
712 {
713 Flags |= Fail;
b2e465d6 714 return _error->Errno("read",_("Read error"));
b0db36b1 715 }
578bfd0a 716
b0db36b1
AL
717 To = (char *)To + Res;
718 Size -= Res;
f604cf55
AL
719 if (Actual != 0)
720 *Actual += Res;
b0db36b1
AL
721 }
722 while (Res > 0 && Size > 0);
723
724 if (Size == 0)
725 return true;
726
ddc1d8d0 727 // Eof handling
f604cf55 728 if (Actual != 0)
ddc1d8d0
AL
729 {
730 Flags |= HitEof;
731 return true;
732 }
733
b0db36b1 734 Flags |= Fail;
b2e465d6 735 return _error->Error(_("read, still have %lu to read but none left"),Size);
578bfd0a
AL
736}
737 /*}}}*/
8e06abb2 738// FileFd::Write - Write to the file /*{{{*/
578bfd0a
AL
739// ---------------------------------------------------------------------
740/* */
a05599f1 741bool FileFd::Write(const void *From,unsigned long Size)
578bfd0a 742{
b0db36b1
AL
743 int Res;
744 errno = 0;
745 do
578bfd0a 746 {
b0db36b1
AL
747 Res = write(iFd,From,Size);
748 if (Res < 0 && errno == EINTR)
749 continue;
750 if (Res < 0)
751 {
752 Flags |= Fail;
b2e465d6 753 return _error->Errno("write",_("Write error"));
b0db36b1
AL
754 }
755
756 From = (char *)From + Res;
757 Size -= Res;
578bfd0a 758 }
b0db36b1 759 while (Res > 0 && Size > 0);
578bfd0a 760
b0db36b1
AL
761 if (Size == 0)
762 return true;
763
764 Flags |= Fail;
b2e465d6 765 return _error->Error(_("write, still have %lu to write but couldn't"),Size);
578bfd0a
AL
766}
767 /*}}}*/
8e06abb2 768// FileFd::Seek - Seek in the file /*{{{*/
578bfd0a
AL
769// ---------------------------------------------------------------------
770/* */
8e06abb2 771bool FileFd::Seek(unsigned long To)
578bfd0a
AL
772{
773 if (lseek(iFd,To,SEEK_SET) != (signed)To)
774 {
775 Flags |= Fail;
b2e465d6 776 return _error->Error("Unable to seek to %lu",To);
578bfd0a
AL
777 }
778
727f18af
AL
779 return true;
780}
781 /*}}}*/
782// FileFd::Skip - Seek in the file /*{{{*/
783// ---------------------------------------------------------------------
784/* */
785bool FileFd::Skip(unsigned long Over)
786{
787 if (lseek(iFd,Over,SEEK_CUR) < 0)
788 {
789 Flags |= Fail;
b2e465d6 790 return _error->Error("Unable to seek ahead %lu",Over);
727f18af
AL
791 }
792
6d5dd02a
AL
793 return true;
794}
795 /*}}}*/
796// FileFd::Truncate - Truncate the file /*{{{*/
797// ---------------------------------------------------------------------
798/* */
799bool FileFd::Truncate(unsigned long To)
800{
801 if (ftruncate(iFd,To) != 0)
802 {
803 Flags |= Fail;
b2e465d6 804 return _error->Error("Unable to truncate to %lu",To);
6d5dd02a
AL
805 }
806
578bfd0a
AL
807 return true;
808}
809 /*}}}*/
7f25bdff
AL
810// FileFd::Tell - Current seek position /*{{{*/
811// ---------------------------------------------------------------------
812/* */
813unsigned long FileFd::Tell()
814{
815 off_t Res = lseek(iFd,0,SEEK_CUR);
816 if (Res == (off_t)-1)
817 _error->Errno("lseek","Failed to determine the current file position");
818 return Res;
819}
820 /*}}}*/
8e06abb2 821// FileFd::Size - Return the size of the file /*{{{*/
578bfd0a
AL
822// ---------------------------------------------------------------------
823/* */
8e06abb2 824unsigned long FileFd::Size()
578bfd0a
AL
825{
826 struct stat Buf;
827 if (fstat(iFd,&Buf) != 0)
828 return _error->Errno("fstat","Unable to determine the file size");
829 return Buf.st_size;
830}
831 /*}}}*/
8e06abb2 832// FileFd::Close - Close the file if the close flag is set /*{{{*/
578bfd0a
AL
833// ---------------------------------------------------------------------
834/* */
8e06abb2 835bool FileFd::Close()
578bfd0a
AL
836{
837 bool Res = true;
838 if ((Flags & AutoClose) == AutoClose)
1164783d 839 if (iFd >= 0 && close(iFd) != 0)
b2e465d6 840 Res &= _error->Errno("close",_("Problem closing the file"));
1164783d
AL
841 iFd = -1;
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 /*}}}*/