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