merged from lp:~donkult/apt/sid
[ntk/apt.git] / methods / bzip2.cc
1 // -*- mode: cpp; mode: fold -*-
2 // Description /*{{{*/
3 /* ######################################################################
4
5 Bzip2 method - Take a file URI in and decompress it into the target
6 file.
7
8 While the method is named "bzip2" it handles also other compression
9 types as it calls binaries based on the name of the method,
10 so it can also be used to handle gzip, lzma and others if named
11 correctly.
12
13 ##################################################################### */
14 /*}}}*/
15 // Include Files /*{{{*/
16 #include <apt-pkg/fileutl.h>
17 #include <apt-pkg/error.h>
18 #include <apt-pkg/acquire-method.h>
19 #include <apt-pkg/strutl.h>
20 #include <apt-pkg/hashes.h>
21
22 #include <sys/stat.h>
23 #include <unistd.h>
24 #include <utime.h>
25 #include <stdio.h>
26 #include <errno.h>
27 #include <apti18n.h>
28 /*}}}*/
29
30 const char *Prog;
31
32 class Bzip2Method : public pkgAcqMethod
33 {
34 virtual bool Fetch(FetchItem *Itm);
35
36 public:
37
38 Bzip2Method() : pkgAcqMethod("1.1",SingleInstance | SendConfig) {};
39 };
40
41
42 // Bzip2Method::Fetch - Decompress the passed URI /*{{{*/
43 // ---------------------------------------------------------------------
44 /* */
45 bool Bzip2Method::Fetch(FetchItem *Itm)
46 {
47 URI Get = Itm->Uri;
48 string Path = Get.Host + Get.Path; // To account for relative paths
49
50 string GzPathOption = "Dir::bin::"+string(Prog);
51
52 FetchResult Res;
53 Res.Filename = Itm->DestFile;
54 URIStart(Res);
55
56 // Open the source and destination files
57 FileFd From(Path,FileFd::ReadOnly);
58
59 // if the file is empty, just rename it and return
60 if(From.Size() == 0)
61 {
62 rename(Path.c_str(), Itm->DestFile.c_str());
63 return true;
64 }
65
66 int GzOut[2];
67 if (pipe(GzOut) < 0)
68 return _error->Errno("pipe",_("Couldn't open pipe for %s"),Prog);
69
70 // Fork bzip2
71 pid_t Process = ExecFork();
72 if (Process == 0)
73 {
74 close(GzOut[0]);
75 dup2(From.Fd(),STDIN_FILENO);
76 dup2(GzOut[1],STDOUT_FILENO);
77 From.Close();
78 close(GzOut[1]);
79 SetCloseExec(STDIN_FILENO,false);
80 SetCloseExec(STDOUT_FILENO,false);
81
82 const char *Args[3];
83 string Tmp = _config->Find(GzPathOption,Prog);
84 Args[0] = Tmp.c_str();
85 Args[1] = "-d";
86 Args[2] = 0;
87 execvp(Args[0],(char **)Args);
88 _exit(100);
89 }
90 From.Close();
91 close(GzOut[1]);
92
93 FileFd FromGz(GzOut[0]); // For autoclose
94 FileFd To(Itm->DestFile,FileFd::WriteAtomic);
95 To.EraseOnFailure();
96 if (_error->PendingError() == true)
97 return false;
98
99 // Read data from bzip2, generate checksums and write
100 Hashes Hash;
101 bool Failed = false;
102 while (1)
103 {
104 unsigned char Buffer[4*1024];
105
106 ssize_t Count = read(GzOut[0],Buffer,sizeof(Buffer));
107 if (Count < 0 && errno == EINTR)
108 continue;
109
110 if (Count < 0)
111 {
112 _error->Errno("read", _("Read error from %s process"),Prog);
113 Failed = true;
114 break;
115 }
116
117 if (Count == 0)
118 break;
119
120 Hash.Add(Buffer,Count);
121 if (To.Write(Buffer,Count) == false)
122 {
123 Failed = true;
124 FromGz.Close();
125 break;
126 }
127 }
128
129 // Wait for bzip2 to finish
130 if (ExecWait(Process,_config->Find(GzPathOption,Prog).c_str(),false) == false)
131 {
132 To.OpFail();
133 return false;
134 }
135
136 To.Close();
137
138 if (Failed == true)
139 return false;
140
141 // Transfer the modification times
142 struct stat Buf;
143 if (stat(Path.c_str(),&Buf) != 0)
144 return _error->Errno("stat",_("Failed to stat"));
145
146 struct utimbuf TimeBuf;
147 TimeBuf.actime = Buf.st_atime;
148 TimeBuf.modtime = Buf.st_mtime;
149 if (utime(Itm->DestFile.c_str(),&TimeBuf) != 0)
150 return _error->Errno("utime",_("Failed to set modification time"));
151
152 if (stat(Itm->DestFile.c_str(),&Buf) != 0)
153 return _error->Errno("stat",_("Failed to stat"));
154
155 // Return a Done response
156 Res.LastModified = Buf.st_mtime;
157 Res.Size = Buf.st_size;
158 Res.TakeHashes(Hash);
159
160 URIDone(Res);
161
162 return true;
163 }
164 /*}}}*/
165
166 int main(int argc, char *argv[])
167 {
168 setlocale(LC_ALL, "");
169
170 Bzip2Method Mth;
171
172 Prog = strrchr(argv[0],'/');
173 Prog++;
174
175 return Mth.Run();
176 }