]> git.saurik.com Git - apt.git/blame - apt-pkg/contrib/fileutl.cc
Minor cleanups, fix for checksum lowercase bug
[apt.git] / apt-pkg / contrib / fileutl.cc
CommitLineData
578bfd0a
AL
1// -*- mode: cpp; mode: fold -*-
2// Description /*{{{*/
ddc1d8d0 3// $Id: fileutl.cc,v 1.30 1999/07/26 17:46:08 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
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 /*{{{*/
6c139d6e 17#ifdef __GNUG__
094a497d 18#pragma implementation "apt-pkg/fileutl.h"
6c139d6e 19#endif
094a497d
AL
20#include <apt-pkg/fileutl.h>
21#include <apt-pkg/error.h>
578bfd0a
AL
22
23#include <unistd.h>
24#include <sys/stat.h>
25#include <sys/fcntl.h>
26#include <sys/types.h>
cc2313b7 27#include <sys/time.h>
54676e1a 28#include <signal.h>
ddc1d8d0 29#include <wait.h>
65a1e968 30#include <errno.h>
578bfd0a
AL
31 /*}}}*/
32
33// CopyFile - Buffered copy of a file /*{{{*/
34// ---------------------------------------------------------------------
35/* The caller is expected to set things so that failure causes erasure */
8b89e57f 36bool CopyFile(FileFd &From,FileFd &To)
578bfd0a
AL
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];
b0db36b1
AL
43 unsigned long Size = From.Size();
44 while (Size != 0)
578bfd0a 45 {
b0db36b1
AL
46 unsigned long ToRead = Size;
47 if (Size > 64000)
48 ToRead = 64000;
49
4a6d5862 50 if (From.Read(Buf,ToRead) == false ||
b0db36b1 51 To.Write(Buf,ToRead) == false)
578bfd0a
AL
52 {
53 delete [] Buf;
54 return false;
55 }
b0db36b1
AL
56
57 Size -= ToRead;
578bfd0a
AL
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. */
70int 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;
c71bc556
AL
82 fl.l_type = F_WRLCK;
83 fl.l_whence = SEEK_SET;
84 fl.l_start = 0;
85 fl.l_len = 0;
578bfd0a
AL
86 if (fcntl(FD,F_SETLK,&fl) == -1)
87 {
d89df07a
AL
88 if (errno == ENOLCK)
89 {
f44344b3 90 _error->Warning("Not using locking for nfs mounted lock file %s",File.c_str());
d89df07a
AL
91 return true;
92 }
578bfd0a
AL
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/* */
105bool 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. */
116string SafeGetCWD()
117{
118 // Stash the current dir.
119 char S[300];
120 S[0] = 0;
7f25bdff 121 if (getcwd(S,sizeof(S)-2) == 0)
578bfd0a 122 return "/";
7f25bdff
AL
123 unsigned int Len = strlen(S);
124 S[Len] = '/';
125 S[Len+1] = 0;
578bfd0a
AL
126 return S;
127}
128 /*}}}*/
8ce4327b
AL
129// flNotDir - Strip the directory from the filename /*{{{*/
130// ---------------------------------------------------------------------
131/* */
132string 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 /*}}}*/
d38b7b3d
AL
141// flNotFile - Strip the file from the directory name /*{{{*/
142// ---------------------------------------------------------------------
143/* */
144string 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 /*}}}*/
3b5421b4
AL
153// SetCloseExec - Set the close on exec flag /*{{{*/
154// ---------------------------------------------------------------------
155/* */
156void 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/* */
168void SetNonBlock(int Fd,bool Block)
169{
0a8a80e5
AL
170 int Flags = fcntl(Fd,F_GETFL) & (~O_NONBLOCK);
171 if (fcntl(Fd,F_SETFL,Flags | ((Block == false)?0:O_NONBLOCK)) != 0)
3b5421b4
AL
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
6d5dd02a
AL
181 applications making use of non-blocking sockets. The timeout is
182 in seconds. */
1084d58a 183bool WaitFd(int Fd,bool write,unsigned long timeout)
3b5421b4
AL
184{
185 fd_set Set;
cc2313b7 186 struct timeval tv;
3b5421b4
AL
187 FD_ZERO(&Set);
188 FD_SET(Fd,&Set);
6d5dd02a
AL
189 tv.tv_sec = timeout;
190 tv.tv_usec = 0;
1084d58a 191 if (write == true)
b0db36b1
AL
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;
1084d58a
AL
202 }
203 else
204 {
b0db36b1
AL
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;
cc2313b7 214 }
1084d58a 215
3b5421b4
AL
216 return true;
217}
218 /*}}}*/
54676e1a
AL
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. */
224int 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 /*}}}*/
ddc1d8d0
AL
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 */
258bool 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 /*}}}*/
578bfd0a 294
13d87e2e 295// FileFd::Open - Open a file /*{{{*/
578bfd0a
AL
296// ---------------------------------------------------------------------
297/* The most commonly used open mode combinations are given with Mode */
13d87e2e 298bool FileFd::Open(string FileName,OpenMode Mode, unsigned long Perms)
578bfd0a 299{
13d87e2e 300 Close();
1164783d 301 Flags = AutoClose;
578bfd0a
AL
302 switch (Mode)
303 {
304 case ReadOnly:
305 iFd = open(FileName.c_str(),O_RDONLY);
306 break;
307
308 case WriteEmpty:
50b513a1
AL
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 }
578bfd0a
AL
316
317 case WriteExists:
318 iFd = open(FileName.c_str(),O_RDWR);
319 break;
0a8e3465
AL
320
321 case WriteAny:
322 iFd = open(FileName.c_str(),O_RDWR | O_CREAT,Perms);
d38b7b3d 323 break;
578bfd0a
AL
324 }
325
326 if (iFd < 0)
13d87e2e
AL
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;
578bfd0a
AL
332}
333 /*}}}*/
8e06abb2 334// FileFd::~File - Closes the file /*{{{*/
578bfd0a
AL
335// ---------------------------------------------------------------------
336/* If the proper modes are selected then we close the Fd and possibly
337 unlink the file on error. */
8e06abb2 338FileFd::~FileFd()
578bfd0a
AL
339{
340 Close();
341}
342 /*}}}*/
8e06abb2 343// FileFd::Read - Read a bit of the file /*{{{*/
578bfd0a 344// ---------------------------------------------------------------------
b0db36b1
AL
345/* We are carefull to handle interruption by a signal while reading
346 gracefully. */
ddc1d8d0 347bool FileFd::Read(void *To,unsigned long Size,bool AllowEof)
578bfd0a 348{
b0db36b1
AL
349 int Res;
350 errno = 0;
351 do
578bfd0a 352 {
b0db36b1
AL
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 }
578bfd0a 361
b0db36b1
AL
362 To = (char *)To + Res;
363 Size -= Res;
364 }
365 while (Res > 0 && Size > 0);
366
367 if (Size == 0)
368 return true;
369
ddc1d8d0
AL
370 // Eof handling
371 if (AllowEof == true)
372 {
373 Flags |= HitEof;
374 return true;
375 }
376
b0db36b1
AL
377 Flags |= Fail;
378 return _error->Error("read, still have %u to read but none left",Size);
578bfd0a
AL
379}
380 /*}}}*/
8e06abb2 381// FileFd::Write - Write to the file /*{{{*/
578bfd0a
AL
382// ---------------------------------------------------------------------
383/* */
a05599f1 384bool FileFd::Write(const void *From,unsigned long Size)
578bfd0a 385{
b0db36b1
AL
386 int Res;
387 errno = 0;
388 do
578bfd0a 389 {
b0db36b1
AL
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;
578bfd0a 401 }
b0db36b1 402 while (Res > 0 && Size > 0);
578bfd0a 403
b0db36b1
AL
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);
578bfd0a
AL
409}
410 /*}}}*/
8e06abb2 411// FileFd::Seek - Seek in the file /*{{{*/
578bfd0a
AL
412// ---------------------------------------------------------------------
413/* */
8e06abb2 414bool FileFd::Seek(unsigned long To)
578bfd0a
AL
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
727f18af
AL
422 return true;
423}
424 /*}}}*/
425// FileFd::Skip - Seek in the file /*{{{*/
426// ---------------------------------------------------------------------
427/* */
428bool 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
6d5dd02a
AL
436 return true;
437}
438 /*}}}*/
439// FileFd::Truncate - Truncate the file /*{{{*/
440// ---------------------------------------------------------------------
441/* */
442bool 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
578bfd0a
AL
450 return true;
451}
452 /*}}}*/
7f25bdff
AL
453// FileFd::Tell - Current seek position /*{{{*/
454// ---------------------------------------------------------------------
455/* */
456unsigned 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 /*}}}*/
8e06abb2 464// FileFd::Size - Return the size of the file /*{{{*/
578bfd0a
AL
465// ---------------------------------------------------------------------
466/* */
8e06abb2 467unsigned long FileFd::Size()
578bfd0a
AL
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 /*}}}*/
8e06abb2 475// FileFd::Close - Close the file if the close flag is set /*{{{*/
578bfd0a
AL
476// ---------------------------------------------------------------------
477/* */
8e06abb2 478bool FileFd::Close()
578bfd0a
AL
479{
480 bool Res = true;
481 if ((Flags & AutoClose) == AutoClose)
1164783d 482 if (iFd >= 0 && close(iFd) != 0)
578bfd0a 483 Res &= _error->Errno("close","Problem closing the file");
1164783d
AL
484 iFd = -1;
485
578bfd0a
AL
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 /*}}}*/