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