]> git.saurik.com Git - apt.git/blob - apt-pkg/contrib/fileutl.cc
0b0739cda5a7ad7d5f0a8f73ffbd07423485f992
[apt.git] / apt-pkg / contrib / fileutl.cc
1 // -*- mode: cpp; mode: fold -*-
2 // Description /*{{{*/
3 // $Id: fileutl.cc,v 1.42 2002/09/14 05:29:22 jgg Exp $
4 /* ######################################################################
5
6 File Utilities
7
8 CopyFile - Buffered copy of a single file
9 GetLock - dpkg compatible lock file manipulation (fcntl)
10
11 Most of this source is placed in the Public Domain, do with it what
12 you will
13 It was originally written by Jason Gunthorpe <jgg@debian.org>.
14
15 The exception is RunScripts() it is under the GPLv2
16
17 ##################################################################### */
18 /*}}}*/
19 // Include Files /*{{{*/
20 #include <apt-pkg/fileutl.h>
21 #include <apt-pkg/strutl.h>
22 #include <apt-pkg/error.h>
23 #include <apt-pkg/sptr.h>
24 #include <apt-pkg/configuration.h>
25
26 #include <apti18n.h>
27
28 #include <cstdlib>
29 #include <cstring>
30 #include <cstdio>
31
32 #include <iostream>
33 #include <unistd.h>
34 #include <fcntl.h>
35 #include <sys/stat.h>
36 #include <sys/types.h>
37 #include <sys/time.h>
38 #include <sys/wait.h>
39 #include <dirent.h>
40 #include <signal.h>
41 #include <errno.h>
42 #include <set>
43 #include <algorithm>
44 /*}}}*/
45
46 using namespace std;
47
48 // RunScripts - Run a set of scripts from a configuration subtree /*{{{*/
49 // ---------------------------------------------------------------------
50 /* */
51 bool 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
110 // CopyFile - Buffered copy of a file /*{{{*/
111 // ---------------------------------------------------------------------
112 /* The caller is expected to set things so that failure causes erasure */
113 bool CopyFile(FileFd &From,FileFd &To)
114 {
115 if (From.IsOpen() == false || To.IsOpen() == false)
116 return false;
117
118 // Buffered copy between fds
119 SPtrArray<unsigned char> Buf = new unsigned char[64000];
120 unsigned long Size = From.Size();
121 while (Size != 0)
122 {
123 unsigned long ToRead = Size;
124 if (Size > 64000)
125 ToRead = 64000;
126
127 if (From.Read(Buf,ToRead) == false ||
128 To.Write(Buf,ToRead) == false)
129 return false;
130
131 Size -= ToRead;
132 }
133
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. */
143 int GetLock(string File,bool Errors)
144 {
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);
148 if (FD < 0)
149 {
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
157 if (Errors == true)
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;
162 return -1;
163 }
164 SetCloseExec(FD,true);
165
166 // Aquire a write lock
167 struct flock fl;
168 fl.l_type = F_WRLCK;
169 fl.l_whence = SEEK_SET;
170 fl.l_start = 0;
171 fl.l_len = 0;
172 if (fcntl(FD,F_SETLK,&fl) == -1)
173 {
174 if (errno == ENOLCK)
175 {
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
178 }
179 if (Errors == true)
180 _error->Errno("open",_("Could not get lock %s"),File.c_str());
181
182 int Tmp = errno;
183 close(FD);
184 errno = Tmp;
185 return -1;
186 }
187
188 return FD;
189 }
190 /*}}}*/
191 // FileExists - Check if a file exists /*{{{*/
192 // ---------------------------------------------------------------------
193 /* */
194 bool FileExists(string File)
195 {
196 struct stat Buf;
197 if (stat(File.c_str(),&Buf) != 0)
198 return false;
199 return true;
200 }
201 /*}}}*/
202 // DirectoryExists - Check if a directory exists and is really one /*{{{*/
203 // ---------------------------------------------------------------------
204 /* */
205 bool 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. */
221 bool 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 /*}}}*/
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. */
257 std::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 }
268 std::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
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 {
294 // skip "hidden" files
295 if (Ent->d_name[0] == '.')
296 continue;
297
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 }
320
321 // Skip bad filenames ala run-parts
322 const char *C = Ent->d_name;
323 for (; *C != 0; ++C)
324 if (isalpha(*C) == 0 && isdigit(*C) == 0
325 && *C != '_' && *C != '-') {
326 // no required extension -> dot is a bad character
327 if (*C == '.' && Ext.empty() == false)
328 continue;
329 break;
330 }
331
332 // we don't reach the end of the name -> bad character included
333 if (*C != 0)
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;
346 continue;
347 }
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)
353 {
354 if (Debug == true)
355 std::clog << "Bad file: " << Ent->d_name << " → stat says not a good file" << std::endl;
356 continue;
357 }
358
359 if (Debug == true)
360 std::clog << "Accept file: " << Ent->d_name << " in " << Dir << std::endl;
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 /*}}}*/
370 // SafeGetCWD - This is a safer getcwd that returns a dynamic string /*{{{*/
371 // ---------------------------------------------------------------------
372 /* We return / on failure. */
373 string SafeGetCWD()
374 {
375 // Stash the current dir.
376 char S[300];
377 S[0] = 0;
378 if (getcwd(S,sizeof(S)-2) == 0)
379 return "/";
380 unsigned int Len = strlen(S);
381 S[Len] = '/';
382 S[Len+1] = 0;
383 return S;
384 }
385 /*}}}*/
386 // flNotDir - Strip the directory from the filename /*{{{*/
387 // ---------------------------------------------------------------------
388 /* */
389 string 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 /*}}}*/
398 // flNotFile - Strip the file from the directory name /*{{{*/
399 // ---------------------------------------------------------------------
400 /* Result ends in a / */
401 string flNotFile(string File)
402 {
403 string::size_type Res = File.rfind('/');
404 if (Res == string::npos)
405 return "./";
406 Res++;
407 return string(File,0,Res);
408 }
409 /*}}}*/
410 // flExtension - Return the extension for the file /*{{{*/
411 // ---------------------------------------------------------------------
412 /* */
413 string 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 /*}}}*/
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. */
425 string 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 /*}}}*/
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. */
465 string 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 /*}}}*/
479 // SetCloseExec - Set the close on exec flag /*{{{*/
480 // ---------------------------------------------------------------------
481 /* */
482 void 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 /* */
494 void SetNonBlock(int Fd,bool Block)
495 {
496 int Flags = fcntl(Fd,F_GETFL) & (~O_NONBLOCK);
497 if (fcntl(Fd,F_SETFL,Flags | ((Block == false)?0:O_NONBLOCK)) != 0)
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 // ---------------------------------------------------------------------
506 /* This waits for a FD to become readable using select. It is useful for
507 applications making use of non-blocking sockets. The timeout is
508 in seconds. */
509 bool WaitFd(int Fd,bool write,unsigned long timeout)
510 {
511 fd_set Set;
512 struct timeval tv;
513 FD_ZERO(&Set);
514 FD_SET(Fd,&Set);
515 tv.tv_sec = timeout;
516 tv.tv_usec = 0;
517 if (write == true)
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;
528 }
529 else
530 {
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;
540 }
541
542 return true;
543 }
544 /*}}}*/
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. */
550 pid_t ExecFork()
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);
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
585 // Close all of our FDs - just in case
586 for (int K = 3; K != 40; K++)
587 {
588 if(KeepFDs.find(K) == KeepFDs.end())
589 fcntl(K,F_SETFD,FD_CLOEXEC);
590 }
591 }
592
593 return Process;
594 }
595 /*}}}*/
596 // ExecWait - Fancy waitpid /*{{{*/
597 // ---------------------------------------------------------------------
598 /* Waits for the given sub process. If Reap is set then no errors are
599 generated. Otherwise a failed subprocess will generate a proper descriptive
600 message */
601 bool ExecWait(pid_t Pid,const char *Name,bool Reap)
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
616 return _error->Error(_("Waited for %s but it wasn't there"),Name);
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;
625 if (WIFSIGNALED(Status) != 0)
626 {
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));
631 }
632
633 if (WIFEXITED(Status) != 0)
634 return _error->Error(_("Sub-process %s returned an error code (%u)"),Name,WEXITSTATUS(Status));
635
636 return _error->Error(_("Sub-process %s exited unexpectedly"),Name);
637 }
638
639 return true;
640 }
641 /*}}}*/
642
643 // FileFd::Open - Open a file /*{{{*/
644 // ---------------------------------------------------------------------
645 /* The most commonly used open mode combinations are given with Mode */
646 bool FileFd::Open(string FileName,OpenMode Mode, unsigned long Perms)
647 {
648 Close();
649 Flags = AutoClose;
650 switch (Mode)
651 {
652 case ReadOnly:
653 iFd = open(FileName.c_str(),O_RDONLY);
654 break;
655
656 case WriteEmpty:
657 {
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);
663 break;
664 }
665
666 case WriteExists:
667 iFd = open(FileName.c_str(),O_RDWR);
668 break;
669
670 case WriteAny:
671 iFd = open(FileName.c_str(),O_RDWR | O_CREAT,Perms);
672 break;
673
674 case WriteTemp:
675 unlink(FileName.c_str());
676 iFd = open(FileName.c_str(),O_RDWR | O_CREAT | O_EXCL,Perms);
677 break;
678 }
679
680 if (iFd < 0)
681 return _error->Errno("open",_("Could not open file %s"),FileName.c_str());
682
683 this->FileName = FileName;
684 SetCloseExec(iFd,true);
685 return true;
686 }
687 /*}}}*/
688 // FileFd::~File - Closes the file /*{{{*/
689 // ---------------------------------------------------------------------
690 /* If the proper modes are selected then we close the Fd and possibly
691 unlink the file on error. */
692 FileFd::~FileFd()
693 {
694 Close();
695 }
696 /*}}}*/
697 // FileFd::Read - Read a bit of the file /*{{{*/
698 // ---------------------------------------------------------------------
699 /* We are carefull to handle interruption by a signal while reading
700 gracefully. */
701 bool FileFd::Read(void *To,unsigned long Size,unsigned long *Actual)
702 {
703 int Res;
704 errno = 0;
705 if (Actual != 0)
706 *Actual = 0;
707
708 do
709 {
710 Res = read(iFd,To,Size);
711 if (Res < 0 && errno == EINTR)
712 continue;
713 if (Res < 0)
714 {
715 Flags |= Fail;
716 return _error->Errno("read",_("Read error"));
717 }
718
719 To = (char *)To + Res;
720 Size -= Res;
721 if (Actual != 0)
722 *Actual += Res;
723 }
724 while (Res > 0 && Size > 0);
725
726 if (Size == 0)
727 return true;
728
729 // Eof handling
730 if (Actual != 0)
731 {
732 Flags |= HitEof;
733 return true;
734 }
735
736 Flags |= Fail;
737 return _error->Error(_("read, still have %lu to read but none left"),Size);
738 }
739 /*}}}*/
740 // FileFd::Write - Write to the file /*{{{*/
741 // ---------------------------------------------------------------------
742 /* */
743 bool FileFd::Write(const void *From,unsigned long Size)
744 {
745 int Res;
746 errno = 0;
747 do
748 {
749 Res = write(iFd,From,Size);
750 if (Res < 0 && errno == EINTR)
751 continue;
752 if (Res < 0)
753 {
754 Flags |= Fail;
755 return _error->Errno("write",_("Write error"));
756 }
757
758 From = (char *)From + Res;
759 Size -= Res;
760 }
761 while (Res > 0 && Size > 0);
762
763 if (Size == 0)
764 return true;
765
766 Flags |= Fail;
767 return _error->Error(_("write, still have %lu to write but couldn't"),Size);
768 }
769 /*}}}*/
770 // FileFd::Seek - Seek in the file /*{{{*/
771 // ---------------------------------------------------------------------
772 /* */
773 bool FileFd::Seek(unsigned long To)
774 {
775 if (lseek(iFd,To,SEEK_SET) != (signed)To)
776 {
777 Flags |= Fail;
778 return _error->Error("Unable to seek to %lu",To);
779 }
780
781 return true;
782 }
783 /*}}}*/
784 // FileFd::Skip - Seek in the file /*{{{*/
785 // ---------------------------------------------------------------------
786 /* */
787 bool FileFd::Skip(unsigned long Over)
788 {
789 if (lseek(iFd,Over,SEEK_CUR) < 0)
790 {
791 Flags |= Fail;
792 return _error->Error("Unable to seek ahead %lu",Over);
793 }
794
795 return true;
796 }
797 /*}}}*/
798 // FileFd::Truncate - Truncate the file /*{{{*/
799 // ---------------------------------------------------------------------
800 /* */
801 bool FileFd::Truncate(unsigned long To)
802 {
803 if (ftruncate(iFd,To) != 0)
804 {
805 Flags |= Fail;
806 return _error->Error("Unable to truncate to %lu",To);
807 }
808
809 return true;
810 }
811 /*}}}*/
812 // FileFd::Tell - Current seek position /*{{{*/
813 // ---------------------------------------------------------------------
814 /* */
815 unsigned 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 /*}}}*/
823 // FileFd::Size - Return the size of the file /*{{{*/
824 // ---------------------------------------------------------------------
825 /* */
826 unsigned long FileFd::Size()
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 /*}}}*/
834 // FileFd::Close - Close the file if the close flag is set /*{{{*/
835 // ---------------------------------------------------------------------
836 /* */
837 bool FileFd::Close()
838 {
839 bool Res = true;
840 if ((Flags & AutoClose) == AutoClose)
841 if (iFd >= 0 && close(iFd) != 0)
842 Res &= _error->Errno("close",_("Problem closing the file"));
843 iFd = -1;
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
851 if ((Flags & Fail) == Fail && (Flags & DelOnFail) == DelOnFail &&
852 FileName.empty() == false)
853 if (unlink(FileName.c_str()) != 0)
854 Res &= _error->WarningE("unlnk",_("Problem unlinking the file"));
855
856
857 return Res;
858 }
859 /*}}}*/
860 // FileFd::Sync - Sync the file /*{{{*/
861 // ---------------------------------------------------------------------
862 /* */
863 bool 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 /*}}}*/