]> 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
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 283 std::vector<string> List;
1408e219 284 Configuration::MatchAgainstConfig SilentIgnore("Dir::Ignore-Files-Silently");
46e39c8e
MV
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;
1408e219 310 _error->Notice("Ignoring file '%s' in directory '%s' as it has no filename extension", Ent->d_name, Dir.c_str());
b39c1859
MV
311 continue;
312 }
313 }
314 else if (std::find(Ext.begin(), Ext.end(), d_ext) == Ext.end())
315 {
316 if (Debug == true)
317 std::clog << "Bad file: " << Ent->d_name << " → bad extension »" << flExtension(Ent->d_name) << "«" << std::endl;
1408e219
DK
318 if (SilentIgnore.Match(Ent->d_name) == false)
319 _error->Notice("Ignoring file '%s' in directory '%s' as it has an invalid filename extension", Ent->d_name, Dir.c_str());
b39c1859
MV
320 continue;
321 }
322 }
46e39c8e 323
b39c1859 324 // Skip bad filenames ala run-parts
46e39c8e
MV
325 const char *C = Ent->d_name;
326 for (; *C != 0; ++C)
327 if (isalpha(*C) == 0 && isdigit(*C) == 0
b39c1859
MV
328 && *C != '_' && *C != '-') {
329 // no required extension -> dot is a bad character
330 if (*C == '.' && Ext.empty() == false)
331 continue;
46e39c8e 332 break;
b39c1859 333 }
46e39c8e 334
b39c1859 335 // we don't reach the end of the name -> bad character included
46e39c8e 336 if (*C != 0)
b39c1859
MV
337 {
338 if (Debug == true)
339 std::clog << "Bad file: " << Ent->d_name << " → bad character »"
340 << *C << "« in filename (period allowed: " << (Ext.empty() ? "no" : "yes") << ")" << std::endl;
341 continue;
342 }
343
344 // skip filenames which end with a period. These are never valid
345 if (*(C - 1) == '.')
346 {
347 if (Debug == true)
348 std::clog << "Bad file: " << Ent->d_name << " → Period as last character" << std::endl;
46e39c8e 349 continue;
b39c1859 350 }
46e39c8e
MV
351
352 // Make sure it is a file and not something else
353 string const File = flCombine(Dir,Ent->d_name);
354 struct stat St;
355 if (stat(File.c_str(),&St) != 0 || S_ISREG(St.st_mode) == 0)
b39c1859
MV
356 {
357 if (Debug == true)
358 std::clog << "Bad file: " << Ent->d_name << " → stat says not a good file" << std::endl;
46e39c8e 359 continue;
b39c1859 360 }
46e39c8e 361
b39c1859
MV
362 if (Debug == true)
363 std::clog << "Accept file: " << Ent->d_name << " in " << Dir << std::endl;
46e39c8e
MV
364 List.push_back(File);
365 }
366 closedir(D);
367
368 if (SortList == true)
369 std::sort(List.begin(),List.end());
370 return List;
371}
372 /*}}}*/
578bfd0a
AL
373// SafeGetCWD - This is a safer getcwd that returns a dynamic string /*{{{*/
374// ---------------------------------------------------------------------
375/* We return / on failure. */
376string SafeGetCWD()
377{
378 // Stash the current dir.
379 char S[300];
380 S[0] = 0;
7f25bdff 381 if (getcwd(S,sizeof(S)-2) == 0)
578bfd0a 382 return "/";
7f25bdff
AL
383 unsigned int Len = strlen(S);
384 S[Len] = '/';
385 S[Len+1] = 0;
578bfd0a
AL
386 return S;
387}
388 /*}}}*/
8ce4327b
AL
389// flNotDir - Strip the directory from the filename /*{{{*/
390// ---------------------------------------------------------------------
391/* */
392string flNotDir(string File)
393{
394 string::size_type Res = File.rfind('/');
395 if (Res == string::npos)
396 return File;
397 Res++;
398 return string(File,Res,Res - File.length());
399}
400 /*}}}*/
d38b7b3d
AL
401// flNotFile - Strip the file from the directory name /*{{{*/
402// ---------------------------------------------------------------------
171c45bc 403/* Result ends in a / */
d38b7b3d
AL
404string flNotFile(string File)
405{
406 string::size_type Res = File.rfind('/');
407 if (Res == string::npos)
171c45bc 408 return "./";
d38b7b3d
AL
409 Res++;
410 return string(File,0,Res);
411}
412 /*}}}*/
b2e465d6
AL
413// flExtension - Return the extension for the file /*{{{*/
414// ---------------------------------------------------------------------
415/* */
416string flExtension(string File)
417{
418 string::size_type Res = File.rfind('.');
419 if (Res == string::npos)
420 return File;
421 Res++;
422 return string(File,Res,Res - File.length());
423}
424 /*}}}*/
421c8d10
AL
425// flNoLink - If file is a symlink then deref it /*{{{*/
426// ---------------------------------------------------------------------
427/* If the name is not a link then the returned path is the input. */
428string flNoLink(string File)
429{
430 struct stat St;
431 if (lstat(File.c_str(),&St) != 0 || S_ISLNK(St.st_mode) == 0)
432 return File;
433 if (stat(File.c_str(),&St) != 0)
434 return File;
435
436 /* Loop resolving the link. There is no need to limit the number of
437 loops because the stat call above ensures that the symlink is not
438 circular */
439 char Buffer[1024];
440 string NFile = File;
441 while (1)
442 {
443 // Read the link
444 int Res;
445 if ((Res = readlink(NFile.c_str(),Buffer,sizeof(Buffer))) <= 0 ||
446 (unsigned)Res >= sizeof(Buffer))
447 return File;
448
449 // Append or replace the previous path
450 Buffer[Res] = 0;
451 if (Buffer[0] == '/')
452 NFile = Buffer;
453 else
454 NFile = flNotFile(NFile) + Buffer;
455
456 // See if we are done
457 if (lstat(NFile.c_str(),&St) != 0)
458 return File;
459 if (S_ISLNK(St.st_mode) == 0)
460 return NFile;
461 }
462}
463 /*}}}*/
b2e465d6
AL
464// flCombine - Combine a file and a directory /*{{{*/
465// ---------------------------------------------------------------------
466/* If the file is an absolute path then it is just returned, otherwise
467 the directory is pre-pended to it. */
468string flCombine(string Dir,string File)
469{
470 if (File.empty() == true)
471 return string();
472
473 if (File[0] == '/' || Dir.empty() == true)
474 return File;
475 if (File.length() >= 2 && File[0] == '.' && File[1] == '/')
476 return File;
477 if (Dir[Dir.length()-1] == '/')
478 return Dir + File;
479 return Dir + '/' + File;
480}
481 /*}}}*/
3b5421b4
AL
482// SetCloseExec - Set the close on exec flag /*{{{*/
483// ---------------------------------------------------------------------
484/* */
485void SetCloseExec(int Fd,bool Close)
486{
487 if (fcntl(Fd,F_SETFD,(Close == false)?0:FD_CLOEXEC) != 0)
488 {
489 cerr << "FATAL -> Could not set close on exec " << strerror(errno) << endl;
490 exit(100);
491 }
492}
493 /*}}}*/
494// SetNonBlock - Set the nonblocking flag /*{{{*/
495// ---------------------------------------------------------------------
496/* */
497void SetNonBlock(int Fd,bool Block)
498{
0a8a80e5
AL
499 int Flags = fcntl(Fd,F_GETFL) & (~O_NONBLOCK);
500 if (fcntl(Fd,F_SETFL,Flags | ((Block == false)?0:O_NONBLOCK)) != 0)
3b5421b4
AL
501 {
502 cerr << "FATAL -> Could not set non-blocking flag " << strerror(errno) << endl;
503 exit(100);
504 }
505}
506 /*}}}*/
507// WaitFd - Wait for a FD to become readable /*{{{*/
508// ---------------------------------------------------------------------
b2e465d6 509/* This waits for a FD to become readable using select. It is useful for
6d5dd02a
AL
510 applications making use of non-blocking sockets. The timeout is
511 in seconds. */
1084d58a 512bool WaitFd(int Fd,bool write,unsigned long timeout)
3b5421b4
AL
513{
514 fd_set Set;
cc2313b7 515 struct timeval tv;
3b5421b4
AL
516 FD_ZERO(&Set);
517 FD_SET(Fd,&Set);
6d5dd02a
AL
518 tv.tv_sec = timeout;
519 tv.tv_usec = 0;
1084d58a 520 if (write == true)
b0db36b1
AL
521 {
522 int Res;
523 do
524 {
525 Res = select(Fd+1,0,&Set,0,(timeout != 0?&tv:0));
526 }
527 while (Res < 0 && errno == EINTR);
528
529 if (Res <= 0)
530 return false;
1084d58a
AL
531 }
532 else
533 {
b0db36b1
AL
534 int Res;
535 do
536 {
537 Res = select(Fd+1,&Set,0,0,(timeout != 0?&tv:0));
538 }
539 while (Res < 0 && errno == EINTR);
540
541 if (Res <= 0)
542 return false;
cc2313b7 543 }
1084d58a 544
3b5421b4
AL
545 return true;
546}
547 /*}}}*/
54676e1a
AL
548// ExecFork - Magical fork that sanitizes the context before execing /*{{{*/
549// ---------------------------------------------------------------------
550/* This is used if you want to cleanse the environment for the forked
551 child, it fixes up the important signals and nukes all of the fds,
552 otherwise acts like normal fork. */
75ef8f14 553pid_t ExecFork()
54676e1a
AL
554{
555 // Fork off the process
556 pid_t Process = fork();
557 if (Process < 0)
558 {
559 cerr << "FATAL -> Failed to fork." << endl;
560 exit(100);
561 }
562
563 // Spawn the subprocess
564 if (Process == 0)
565 {
566 // Setup the signals
567 signal(SIGPIPE,SIG_DFL);
568 signal(SIGQUIT,SIG_DFL);
569 signal(SIGINT,SIG_DFL);
570 signal(SIGWINCH,SIG_DFL);
571 signal(SIGCONT,SIG_DFL);
572 signal(SIGTSTP,SIG_DFL);
75ef8f14
MV
573
574 set<int> KeepFDs;
575 Configuration::Item const *Opts = _config->Tree("APT::Keep-Fds");
576 if (Opts != 0 && Opts->Child != 0)
577 {
578 Opts = Opts->Child;
579 for (; Opts != 0; Opts = Opts->Next)
580 {
581 if (Opts->Value.empty() == true)
582 continue;
583 int fd = atoi(Opts->Value.c_str());
584 KeepFDs.insert(fd);
585 }
586 }
587
54676e1a
AL
588 // Close all of our FDs - just in case
589 for (int K = 3; K != 40; K++)
75ef8f14
MV
590 {
591 if(KeepFDs.find(K) == KeepFDs.end())
007dc9e0 592 fcntl(K,F_SETFD,FD_CLOEXEC);
75ef8f14 593 }
54676e1a
AL
594 }
595
596 return Process;
597}
598 /*}}}*/
ddc1d8d0
AL
599// ExecWait - Fancy waitpid /*{{{*/
600// ---------------------------------------------------------------------
2c9a72d1 601/* Waits for the given sub process. If Reap is set then no errors are
ddc1d8d0
AL
602 generated. Otherwise a failed subprocess will generate a proper descriptive
603 message */
3826564e 604bool ExecWait(pid_t Pid,const char *Name,bool Reap)
ddc1d8d0
AL
605{
606 if (Pid <= 1)
607 return true;
608
609 // Wait and collect the error code
610 int Status;
611 while (waitpid(Pid,&Status,0) != Pid)
612 {
613 if (errno == EINTR)
614 continue;
615
616 if (Reap == true)
617 return false;
618
db0db9fe 619 return _error->Error(_("Waited for %s but it wasn't there"),Name);
ddc1d8d0
AL
620 }
621
622
623 // Check for an error code.
624 if (WIFEXITED(Status) == 0 || WEXITSTATUS(Status) != 0)
625 {
626 if (Reap == true)
627 return false;
ab7f4d7c 628 if (WIFSIGNALED(Status) != 0)
40e7fe0e 629 {
ab7f4d7c
MV
630 if( WTERMSIG(Status) == SIGSEGV)
631 return _error->Error(_("Sub-process %s received a segmentation fault."),Name);
632 else
633 return _error->Error(_("Sub-process %s received signal %u."),Name, WTERMSIG(Status));
40e7fe0e 634 }
ddc1d8d0
AL
635
636 if (WIFEXITED(Status) != 0)
b2e465d6 637 return _error->Error(_("Sub-process %s returned an error code (%u)"),Name,WEXITSTATUS(Status));
ddc1d8d0 638
b2e465d6 639 return _error->Error(_("Sub-process %s exited unexpectedly"),Name);
ddc1d8d0
AL
640 }
641
642 return true;
643}
644 /*}}}*/
578bfd0a 645
13d87e2e 646// FileFd::Open - Open a file /*{{{*/
578bfd0a
AL
647// ---------------------------------------------------------------------
648/* The most commonly used open mode combinations are given with Mode */
13d87e2e 649bool FileFd::Open(string FileName,OpenMode Mode, unsigned long Perms)
578bfd0a 650{
13d87e2e 651 Close();
1164783d 652 Flags = AutoClose;
578bfd0a
AL
653 switch (Mode)
654 {
655 case ReadOnly:
656 iFd = open(FileName.c_str(),O_RDONLY);
657 break;
658
659 case WriteEmpty:
50b513a1
AL
660 {
661 struct stat Buf;
459681d3 662 if (lstat(FileName.c_str(),&Buf) == 0 && S_ISLNK(Buf.st_mode))
50b513a1
AL
663 unlink(FileName.c_str());
664 iFd = open(FileName.c_str(),O_RDWR | O_CREAT | O_TRUNC,Perms);
665 break;
666 }
578bfd0a
AL
667
668 case WriteExists:
669 iFd = open(FileName.c_str(),O_RDWR);
670 break;
0a8e3465
AL
671
672 case WriteAny:
673 iFd = open(FileName.c_str(),O_RDWR | O_CREAT,Perms);
d38b7b3d 674 break;
f08fcf34
AL
675
676 case WriteTemp:
4decd43c
AL
677 unlink(FileName.c_str());
678 iFd = open(FileName.c_str(),O_RDWR | O_CREAT | O_EXCL,Perms);
f08fcf34 679 break;
578bfd0a
AL
680 }
681
682 if (iFd < 0)
b2e465d6 683 return _error->Errno("open",_("Could not open file %s"),FileName.c_str());
13d87e2e
AL
684
685 this->FileName = FileName;
686 SetCloseExec(iFd,true);
687 return true;
578bfd0a
AL
688}
689 /*}}}*/
8e06abb2 690// FileFd::~File - Closes the file /*{{{*/
578bfd0a
AL
691// ---------------------------------------------------------------------
692/* If the proper modes are selected then we close the Fd and possibly
693 unlink the file on error. */
8e06abb2 694FileFd::~FileFd()
578bfd0a
AL
695{
696 Close();
697}
698 /*}}}*/
8e06abb2 699// FileFd::Read - Read a bit of the file /*{{{*/
578bfd0a 700// ---------------------------------------------------------------------
b0db36b1
AL
701/* We are carefull to handle interruption by a signal while reading
702 gracefully. */
f604cf55 703bool FileFd::Read(void *To,unsigned long Size,unsigned long *Actual)
578bfd0a 704{
b0db36b1
AL
705 int Res;
706 errno = 0;
f604cf55
AL
707 if (Actual != 0)
708 *Actual = 0;
709
b0db36b1 710 do
578bfd0a 711 {
b0db36b1
AL
712 Res = read(iFd,To,Size);
713 if (Res < 0 && errno == EINTR)
714 continue;
715 if (Res < 0)
716 {
717 Flags |= Fail;
b2e465d6 718 return _error->Errno("read",_("Read error"));
b0db36b1 719 }
578bfd0a 720
b0db36b1
AL
721 To = (char *)To + Res;
722 Size -= Res;
f604cf55
AL
723 if (Actual != 0)
724 *Actual += Res;
b0db36b1
AL
725 }
726 while (Res > 0 && Size > 0);
727
728 if (Size == 0)
729 return true;
730
ddc1d8d0 731 // Eof handling
f604cf55 732 if (Actual != 0)
ddc1d8d0
AL
733 {
734 Flags |= HitEof;
735 return true;
736 }
737
b0db36b1 738 Flags |= Fail;
b2e465d6 739 return _error->Error(_("read, still have %lu to read but none left"),Size);
578bfd0a
AL
740}
741 /*}}}*/
8e06abb2 742// FileFd::Write - Write to the file /*{{{*/
578bfd0a
AL
743// ---------------------------------------------------------------------
744/* */
a05599f1 745bool FileFd::Write(const void *From,unsigned long Size)
578bfd0a 746{
b0db36b1
AL
747 int Res;
748 errno = 0;
749 do
578bfd0a 750 {
b0db36b1
AL
751 Res = write(iFd,From,Size);
752 if (Res < 0 && errno == EINTR)
753 continue;
754 if (Res < 0)
755 {
756 Flags |= Fail;
b2e465d6 757 return _error->Errno("write",_("Write error"));
b0db36b1
AL
758 }
759
760 From = (char *)From + Res;
761 Size -= Res;
578bfd0a 762 }
b0db36b1 763 while (Res > 0 && Size > 0);
578bfd0a 764
b0db36b1
AL
765 if (Size == 0)
766 return true;
767
768 Flags |= Fail;
b2e465d6 769 return _error->Error(_("write, still have %lu to write but couldn't"),Size);
578bfd0a
AL
770}
771 /*}}}*/
8e06abb2 772// FileFd::Seek - Seek in the file /*{{{*/
578bfd0a
AL
773// ---------------------------------------------------------------------
774/* */
8e06abb2 775bool FileFd::Seek(unsigned long To)
578bfd0a
AL
776{
777 if (lseek(iFd,To,SEEK_SET) != (signed)To)
778 {
779 Flags |= Fail;
b2e465d6 780 return _error->Error("Unable to seek to %lu",To);
578bfd0a
AL
781 }
782
727f18af
AL
783 return true;
784}
785 /*}}}*/
786// FileFd::Skip - Seek in the file /*{{{*/
787// ---------------------------------------------------------------------
788/* */
789bool FileFd::Skip(unsigned long Over)
790{
791 if (lseek(iFd,Over,SEEK_CUR) < 0)
792 {
793 Flags |= Fail;
b2e465d6 794 return _error->Error("Unable to seek ahead %lu",Over);
727f18af
AL
795 }
796
6d5dd02a
AL
797 return true;
798}
799 /*}}}*/
800// FileFd::Truncate - Truncate the file /*{{{*/
801// ---------------------------------------------------------------------
802/* */
803bool FileFd::Truncate(unsigned long To)
804{
805 if (ftruncate(iFd,To) != 0)
806 {
807 Flags |= Fail;
b2e465d6 808 return _error->Error("Unable to truncate to %lu",To);
6d5dd02a
AL
809 }
810
578bfd0a
AL
811 return true;
812}
813 /*}}}*/
7f25bdff
AL
814// FileFd::Tell - Current seek position /*{{{*/
815// ---------------------------------------------------------------------
816/* */
817unsigned long FileFd::Tell()
818{
819 off_t Res = lseek(iFd,0,SEEK_CUR);
820 if (Res == (off_t)-1)
821 _error->Errno("lseek","Failed to determine the current file position");
822 return Res;
823}
824 /*}}}*/
8e06abb2 825// FileFd::Size - Return the size of the file /*{{{*/
578bfd0a
AL
826// ---------------------------------------------------------------------
827/* */
8e06abb2 828unsigned long FileFd::Size()
578bfd0a
AL
829{
830 struct stat Buf;
831 if (fstat(iFd,&Buf) != 0)
832 return _error->Errno("fstat","Unable to determine the file size");
833 return Buf.st_size;
834}
835 /*}}}*/
8e06abb2 836// FileFd::Close - Close the file if the close flag is set /*{{{*/
578bfd0a
AL
837// ---------------------------------------------------------------------
838/* */
8e06abb2 839bool FileFd::Close()
578bfd0a
AL
840{
841 bool Res = true;
842 if ((Flags & AutoClose) == AutoClose)
1164783d 843 if (iFd >= 0 && close(iFd) != 0)
b2e465d6 844 Res &= _error->Errno("close",_("Problem closing the file"));
1164783d
AL
845 iFd = -1;
846
578bfd0a
AL
847 if ((Flags & Fail) == Fail && (Flags & DelOnFail) == DelOnFail &&
848 FileName.empty() == false)
849 if (unlink(FileName.c_str()) != 0)
b2e465d6 850 Res &= _error->WarningE("unlnk",_("Problem unlinking the file"));
578bfd0a
AL
851 return Res;
852}
853 /*}}}*/
b2e465d6
AL
854// FileFd::Sync - Sync the file /*{{{*/
855// ---------------------------------------------------------------------
856/* */
857bool FileFd::Sync()
858{
859#ifdef _POSIX_SYNCHRONIZED_IO
860 if (fsync(iFd) != 0)
861 return _error->Errno("sync",_("Problem syncing the file"));
862#endif
863 return true;
864}
865 /*}}}*/