]> git.saurik.com Git - apt.git/blob - apt-pkg/contrib/fileutl.cc
Speeling correction
[apt.git] / apt-pkg / contrib / fileutl.cc
1 // -*- mode: cpp; mode: fold -*-
2 // Description /*{{{*/
3 // $Id: fileutl.cc,v 1.40 2002/04/18 05:08:49 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 This source is placed in the Public Domain, do with it what you will
12 It was originally written by Jason Gunthorpe.
13
14 ##################################################################### */
15 /*}}}*/
16 // Include Files /*{{{*/
17 #ifdef __GNUG__
18 #pragma implementation "apt-pkg/fileutl.h"
19 #endif
20 #include <apt-pkg/fileutl.h>
21 #include <apt-pkg/error.h>
22 #include <apt-pkg/sptr.h>
23
24 #include <apti18n.h>
25
26 #include <iostream>
27 #include <unistd.h>
28 #include <fcntl.h>
29 #include <sys/stat.h>
30 #include <sys/types.h>
31 #include <sys/time.h>
32 #include <sys/wait.h>
33 #include <signal.h>
34 #include <errno.h>
35 /*}}}*/
36
37 using namespace std;
38
39 // CopyFile - Buffered copy of a file /*{{{*/
40 // ---------------------------------------------------------------------
41 /* The caller is expected to set things so that failure causes erasure */
42 bool CopyFile(FileFd &From,FileFd &To)
43 {
44 if (From.IsOpen() == false || To.IsOpen() == false)
45 return false;
46
47 // Buffered copy between fds
48 SPtrArray<unsigned char> Buf = new unsigned char[64000];
49 unsigned long Size = From.Size();
50 while (Size != 0)
51 {
52 unsigned long ToRead = Size;
53 if (Size > 64000)
54 ToRead = 64000;
55
56 if (From.Read(Buf,ToRead) == false ||
57 To.Write(Buf,ToRead) == false)
58 return false;
59
60 Size -= ToRead;
61 }
62
63 return true;
64 }
65 /*}}}*/
66 // GetLock - Gets a lock file /*{{{*/
67 // ---------------------------------------------------------------------
68 /* This will create an empty file of the given name and lock it. Once this
69 is done all other calls to GetLock in any other process will fail with
70 -1. The return result is the fd of the file, the call should call
71 close at some time. */
72 int GetLock(string File,bool Errors)
73 {
74 int FD = open(File.c_str(),O_RDWR | O_CREAT | O_TRUNC,0640);
75 if (FD < 0)
76 {
77 // Read only .. cant have locking problems there.
78 if (errno == EROFS)
79 {
80 _error->Warning(_("Not using locking for read only lock file %s"),File.c_str());
81 return dup(0); // Need something for the caller to close
82 }
83
84 if (Errors == true)
85 _error->Errno("open",_("Could not open lock file %s"),File.c_str());
86
87 // Feh.. We do this to distinguish the lock vs open case..
88 errno = EPERM;
89 return -1;
90 }
91 SetCloseExec(FD,true);
92
93 // Aquire a write lock
94 struct flock fl;
95 fl.l_type = F_WRLCK;
96 fl.l_whence = SEEK_SET;
97 fl.l_start = 0;
98 fl.l_len = 0;
99 if (fcntl(FD,F_SETLK,&fl) == -1)
100 {
101 if (errno == ENOLCK)
102 {
103 _error->Warning(_("Not using locking for nfs mounted lock file %s"),File.c_str());
104 return dup(0); // Need something for the caller to close
105 }
106 if (Errors == true)
107 _error->Errno("open",_("Could not get lock %s"),File.c_str());
108
109 int Tmp = errno;
110 close(FD);
111 errno = Tmp;
112 return -1;
113 }
114
115 return FD;
116 }
117 /*}}}*/
118 // FileExists - Check if a file exists /*{{{*/
119 // ---------------------------------------------------------------------
120 /* */
121 bool FileExists(string File)
122 {
123 struct stat Buf;
124 if (stat(File.c_str(),&Buf) != 0)
125 return false;
126 return true;
127 }
128 /*}}}*/
129 // SafeGetCWD - This is a safer getcwd that returns a dynamic string /*{{{*/
130 // ---------------------------------------------------------------------
131 /* We return / on failure. */
132 string SafeGetCWD()
133 {
134 // Stash the current dir.
135 char S[300];
136 S[0] = 0;
137 if (getcwd(S,sizeof(S)-2) == 0)
138 return "/";
139 unsigned int Len = strlen(S);
140 S[Len] = '/';
141 S[Len+1] = 0;
142 return S;
143 }
144 /*}}}*/
145 // flNotDir - Strip the directory from the filename /*{{{*/
146 // ---------------------------------------------------------------------
147 /* */
148 string flNotDir(string File)
149 {
150 string::size_type Res = File.rfind('/');
151 if (Res == string::npos)
152 return File;
153 Res++;
154 return string(File,Res,Res - File.length());
155 }
156 /*}}}*/
157 // flNotFile - Strip the file from the directory name /*{{{*/
158 // ---------------------------------------------------------------------
159 /* */
160 string flNotFile(string File)
161 {
162 string::size_type Res = File.rfind('/');
163 if (Res == string::npos)
164 return File;
165 Res++;
166 return string(File,0,Res);
167 }
168 /*}}}*/
169 // flExtension - Return the extension for the file /*{{{*/
170 // ---------------------------------------------------------------------
171 /* */
172 string flExtension(string File)
173 {
174 string::size_type Res = File.rfind('.');
175 if (Res == string::npos)
176 return File;
177 Res++;
178 return string(File,Res,Res - File.length());
179 }
180 /*}}}*/
181 // flNoLink - If file is a symlink then deref it /*{{{*/
182 // ---------------------------------------------------------------------
183 /* If the name is not a link then the returned path is the input. */
184 string flNoLink(string File)
185 {
186 struct stat St;
187 if (lstat(File.c_str(),&St) != 0 || S_ISLNK(St.st_mode) == 0)
188 return File;
189 if (stat(File.c_str(),&St) != 0)
190 return File;
191
192 /* Loop resolving the link. There is no need to limit the number of
193 loops because the stat call above ensures that the symlink is not
194 circular */
195 char Buffer[1024];
196 string NFile = File;
197 while (1)
198 {
199 // Read the link
200 int Res;
201 if ((Res = readlink(NFile.c_str(),Buffer,sizeof(Buffer))) <= 0 ||
202 (unsigned)Res >= sizeof(Buffer))
203 return File;
204
205 // Append or replace the previous path
206 Buffer[Res] = 0;
207 if (Buffer[0] == '/')
208 NFile = Buffer;
209 else
210 NFile = flNotFile(NFile) + Buffer;
211
212 // See if we are done
213 if (lstat(NFile.c_str(),&St) != 0)
214 return File;
215 if (S_ISLNK(St.st_mode) == 0)
216 return NFile;
217 }
218 }
219 /*}}}*/
220 // flCombine - Combine a file and a directory /*{{{*/
221 // ---------------------------------------------------------------------
222 /* If the file is an absolute path then it is just returned, otherwise
223 the directory is pre-pended to it. */
224 string flCombine(string Dir,string File)
225 {
226 if (File.empty() == true)
227 return string();
228
229 if (File[0] == '/' || Dir.empty() == true)
230 return File;
231 if (File.length() >= 2 && File[0] == '.' && File[1] == '/')
232 return File;
233 if (Dir[Dir.length()-1] == '/')
234 return Dir + File;
235 return Dir + '/' + File;
236 }
237 /*}}}*/
238 // SetCloseExec - Set the close on exec flag /*{{{*/
239 // ---------------------------------------------------------------------
240 /* */
241 void SetCloseExec(int Fd,bool Close)
242 {
243 if (fcntl(Fd,F_SETFD,(Close == false)?0:FD_CLOEXEC) != 0)
244 {
245 cerr << "FATAL -> Could not set close on exec " << strerror(errno) << endl;
246 exit(100);
247 }
248 }
249 /*}}}*/
250 // SetNonBlock - Set the nonblocking flag /*{{{*/
251 // ---------------------------------------------------------------------
252 /* */
253 void SetNonBlock(int Fd,bool Block)
254 {
255 int Flags = fcntl(Fd,F_GETFL) & (~O_NONBLOCK);
256 if (fcntl(Fd,F_SETFL,Flags | ((Block == false)?0:O_NONBLOCK)) != 0)
257 {
258 cerr << "FATAL -> Could not set non-blocking flag " << strerror(errno) << endl;
259 exit(100);
260 }
261 }
262 /*}}}*/
263 // WaitFd - Wait for a FD to become readable /*{{{*/
264 // ---------------------------------------------------------------------
265 /* This waits for a FD to become readable using select. It is useful for
266 applications making use of non-blocking sockets. The timeout is
267 in seconds. */
268 bool WaitFd(int Fd,bool write,unsigned long timeout)
269 {
270 fd_set Set;
271 struct timeval tv;
272 FD_ZERO(&Set);
273 FD_SET(Fd,&Set);
274 tv.tv_sec = timeout;
275 tv.tv_usec = 0;
276 if (write == true)
277 {
278 int Res;
279 do
280 {
281 Res = select(Fd+1,0,&Set,0,(timeout != 0?&tv:0));
282 }
283 while (Res < 0 && errno == EINTR);
284
285 if (Res <= 0)
286 return false;
287 }
288 else
289 {
290 int Res;
291 do
292 {
293 Res = select(Fd+1,&Set,0,0,(timeout != 0?&tv:0));
294 }
295 while (Res < 0 && errno == EINTR);
296
297 if (Res <= 0)
298 return false;
299 }
300
301 return true;
302 }
303 /*}}}*/
304 // ExecFork - Magical fork that sanitizes the context before execing /*{{{*/
305 // ---------------------------------------------------------------------
306 /* This is used if you want to cleanse the environment for the forked
307 child, it fixes up the important signals and nukes all of the fds,
308 otherwise acts like normal fork. */
309 int ExecFork()
310 {
311 // Fork off the process
312 pid_t Process = fork();
313 if (Process < 0)
314 {
315 cerr << "FATAL -> Failed to fork." << endl;
316 exit(100);
317 }
318
319 // Spawn the subprocess
320 if (Process == 0)
321 {
322 // Setup the signals
323 signal(SIGPIPE,SIG_DFL);
324 signal(SIGQUIT,SIG_DFL);
325 signal(SIGINT,SIG_DFL);
326 signal(SIGWINCH,SIG_DFL);
327 signal(SIGCONT,SIG_DFL);
328 signal(SIGTSTP,SIG_DFL);
329
330 // Close all of our FDs - just in case
331 for (int K = 3; K != 40; K++)
332 fcntl(K,F_SETFD,FD_CLOEXEC);
333 }
334
335 return Process;
336 }
337 /*}}}*/
338 // ExecWait - Fancy waitpid /*{{{*/
339 // ---------------------------------------------------------------------
340 /* Waits for the given sub process. If Reap is set then no errors are
341 generated. Otherwise a failed subprocess will generate a proper descriptive
342 message */
343 bool ExecWait(int Pid,const char *Name,bool Reap)
344 {
345 if (Pid <= 1)
346 return true;
347
348 // Wait and collect the error code
349 int Status;
350 while (waitpid(Pid,&Status,0) != Pid)
351 {
352 if (errno == EINTR)
353 continue;
354
355 if (Reap == true)
356 return false;
357
358 return _error->Error(_("Waited, for %s but it wasn't there"),Name);
359 }
360
361
362 // Check for an error code.
363 if (WIFEXITED(Status) == 0 || WEXITSTATUS(Status) != 0)
364 {
365 if (Reap == true)
366 return false;
367 if (WIFSIGNALED(Status) != 0 && WTERMSIG(Status) == SIGSEGV)
368 return _error->Error(_("Sub-process %s received a segmentation fault."),Name);
369
370 if (WIFEXITED(Status) != 0)
371 return _error->Error(_("Sub-process %s returned an error code (%u)"),Name,WEXITSTATUS(Status));
372
373 return _error->Error(_("Sub-process %s exited unexpectedly"),Name);
374 }
375
376 return true;
377 }
378 /*}}}*/
379
380 // FileFd::Open - Open a file /*{{{*/
381 // ---------------------------------------------------------------------
382 /* The most commonly used open mode combinations are given with Mode */
383 bool FileFd::Open(string FileName,OpenMode Mode, unsigned long Perms)
384 {
385 Close();
386 Flags = AutoClose;
387 switch (Mode)
388 {
389 case ReadOnly:
390 iFd = open(FileName.c_str(),O_RDONLY);
391 break;
392
393 case WriteEmpty:
394 {
395 struct stat Buf;
396 if (lstat(FileName.c_str(),&Buf) == 0 && S_ISLNK(Buf.st_mode))
397 unlink(FileName.c_str());
398 iFd = open(FileName.c_str(),O_RDWR | O_CREAT | O_TRUNC,Perms);
399 break;
400 }
401
402 case WriteExists:
403 iFd = open(FileName.c_str(),O_RDWR);
404 break;
405
406 case WriteAny:
407 iFd = open(FileName.c_str(),O_RDWR | O_CREAT,Perms);
408 break;
409
410 case WriteTemp:
411 unlink(FileName.c_str());
412 iFd = open(FileName.c_str(),O_RDWR | O_CREAT | O_EXCL,Perms);
413 break;
414 }
415
416 if (iFd < 0)
417 return _error->Errno("open",_("Could not open file %s"),FileName.c_str());
418
419 this->FileName = FileName;
420 SetCloseExec(iFd,true);
421 return true;
422 }
423 /*}}}*/
424 // FileFd::~File - Closes the file /*{{{*/
425 // ---------------------------------------------------------------------
426 /* If the proper modes are selected then we close the Fd and possibly
427 unlink the file on error. */
428 FileFd::~FileFd()
429 {
430 Close();
431 }
432 /*}}}*/
433 // FileFd::Read - Read a bit of the file /*{{{*/
434 // ---------------------------------------------------------------------
435 /* We are carefull to handle interruption by a signal while reading
436 gracefully. */
437 bool FileFd::Read(void *To,unsigned long Size,unsigned long *Actual)
438 {
439 int Res;
440 errno = 0;
441 if (Actual != 0)
442 *Actual = 0;
443
444 do
445 {
446 Res = read(iFd,To,Size);
447 if (Res < 0 && errno == EINTR)
448 continue;
449 if (Res < 0)
450 {
451 Flags |= Fail;
452 return _error->Errno("read",_("Read error"));
453 }
454
455 To = (char *)To + Res;
456 Size -= Res;
457 if (Actual != 0)
458 *Actual += Res;
459 }
460 while (Res > 0 && Size > 0);
461
462 if (Size == 0)
463 return true;
464
465 // Eof handling
466 if (Actual != 0)
467 {
468 Flags |= HitEof;
469 return true;
470 }
471
472 Flags |= Fail;
473 return _error->Error(_("read, still have %lu to read but none left"),Size);
474 }
475 /*}}}*/
476 // FileFd::Write - Write to the file /*{{{*/
477 // ---------------------------------------------------------------------
478 /* */
479 bool FileFd::Write(const void *From,unsigned long Size)
480 {
481 int Res;
482 errno = 0;
483 do
484 {
485 Res = write(iFd,From,Size);
486 if (Res < 0 && errno == EINTR)
487 continue;
488 if (Res < 0)
489 {
490 Flags |= Fail;
491 return _error->Errno("write",_("Write error"));
492 }
493
494 From = (char *)From + Res;
495 Size -= Res;
496 }
497 while (Res > 0 && Size > 0);
498
499 if (Size == 0)
500 return true;
501
502 Flags |= Fail;
503 return _error->Error(_("write, still have %lu to write but couldn't"),Size);
504 }
505 /*}}}*/
506 // FileFd::Seek - Seek in the file /*{{{*/
507 // ---------------------------------------------------------------------
508 /* */
509 bool FileFd::Seek(unsigned long To)
510 {
511 if (lseek(iFd,To,SEEK_SET) != (signed)To)
512 {
513 Flags |= Fail;
514 return _error->Error("Unable to seek to %lu",To);
515 }
516
517 return true;
518 }
519 /*}}}*/
520 // FileFd::Skip - Seek in the file /*{{{*/
521 // ---------------------------------------------------------------------
522 /* */
523 bool FileFd::Skip(unsigned long Over)
524 {
525 if (lseek(iFd,Over,SEEK_CUR) < 0)
526 {
527 Flags |= Fail;
528 return _error->Error("Unable to seek ahead %lu",Over);
529 }
530
531 return true;
532 }
533 /*}}}*/
534 // FileFd::Truncate - Truncate the file /*{{{*/
535 // ---------------------------------------------------------------------
536 /* */
537 bool FileFd::Truncate(unsigned long To)
538 {
539 if (ftruncate(iFd,To) != 0)
540 {
541 Flags |= Fail;
542 return _error->Error("Unable to truncate to %lu",To);
543 }
544
545 return true;
546 }
547 /*}}}*/
548 // FileFd::Tell - Current seek position /*{{{*/
549 // ---------------------------------------------------------------------
550 /* */
551 unsigned long FileFd::Tell()
552 {
553 off_t Res = lseek(iFd,0,SEEK_CUR);
554 if (Res == (off_t)-1)
555 _error->Errno("lseek","Failed to determine the current file position");
556 return Res;
557 }
558 /*}}}*/
559 // FileFd::Size - Return the size of the file /*{{{*/
560 // ---------------------------------------------------------------------
561 /* */
562 unsigned long FileFd::Size()
563 {
564 struct stat Buf;
565 if (fstat(iFd,&Buf) != 0)
566 return _error->Errno("fstat","Unable to determine the file size");
567 return Buf.st_size;
568 }
569 /*}}}*/
570 // FileFd::Close - Close the file if the close flag is set /*{{{*/
571 // ---------------------------------------------------------------------
572 /* */
573 bool FileFd::Close()
574 {
575 bool Res = true;
576 if ((Flags & AutoClose) == AutoClose)
577 if (iFd >= 0 && close(iFd) != 0)
578 Res &= _error->Errno("close",_("Problem closing the file"));
579 iFd = -1;
580
581 if ((Flags & Fail) == Fail && (Flags & DelOnFail) == DelOnFail &&
582 FileName.empty() == false)
583 if (unlink(FileName.c_str()) != 0)
584 Res &= _error->WarningE("unlnk",_("Problem unlinking the file"));
585 return Res;
586 }
587 /*}}}*/
588 // FileFd::Sync - Sync the file /*{{{*/
589 // ---------------------------------------------------------------------
590 /* */
591 bool FileFd::Sync()
592 {
593 #ifdef _POSIX_SYNCHRONIZED_IO
594 if (fsync(iFd) != 0)
595 return _error->Errno("sync",_("Problem syncing the file"));
596 #endif
597 return true;
598 }
599 /*}}}*/