add hashsum support in apt-file download and add more tests
[ntk/apt.git] / apt-pkg / contrib / cdromutl.cc
1 // -*- mode: cpp; mode: fold -*-
2 // Description /*{{{*/
3 // $Id: cdromutl.cc,v 1.12 2001/02/20 07:03:17 jgg Exp $
4 /* ######################################################################
5
6 CDROM Utilities - Some functions to manipulate CDROM mounts.
7
8 These are here for the cdrom method and apt-cdrom.
9
10 ##################################################################### */
11 /*}}}*/
12 // Include Files /*{{{*/
13 #include<config.h>
14
15 #include <apt-pkg/cdromutl.h>
16 #include <apt-pkg/error.h>
17 #include <apt-pkg/md5.h>
18 #include <apt-pkg/fileutl.h>
19 #include <apt-pkg/configuration.h>
20 #include <apt-pkg/strutl.h>
21
22 #include <sys/wait.h>
23 #include <sys/statvfs.h>
24 #include <dirent.h>
25 #include <fcntl.h>
26 #include <sys/stat.h>
27 #include <unistd.h>
28 #include <stdio.h>
29
30 #include <apti18n.h>
31 /*}}}*/
32
33 using std::string;
34
35 // IsMounted - Returns true if the mount point is mounted /*{{{*/
36 // ---------------------------------------------------------------------
37 /* This is a simple algorithm that should always work, we stat the mount point
38 and the '..' file in the mount point and see if they are on the same device.
39 By definition if they are the same then it is not mounted. This should
40 account for symlinked mount points as well. */
41 bool IsMounted(string &Path)
42 {
43 if (Path.empty() == true)
44 return false;
45
46 // Need that trailing slash for directories
47 if (Path[Path.length() - 1] != '/')
48 Path += '/';
49
50 /* First we check if the path is actually mounted, we do this by
51 stating the path and the previous directory (careful of links!)
52 and comparing their device fields. */
53 struct stat Buf,Buf2;
54 if (stat(Path.c_str(),&Buf) != 0 ||
55 stat((Path + "../").c_str(),&Buf2) != 0)
56 return _error->Errno("stat",_("Unable to stat the mount point %s"),Path.c_str());
57
58 if (Buf.st_dev == Buf2.st_dev)
59 return false;
60 return true;
61 }
62 /*}}}*/
63 // UnmountCdrom - Unmount a cdrom /*{{{*/
64 // ---------------------------------------------------------------------
65 /* Forking umount works much better than the umount syscall which can
66 leave /etc/mtab inconsitant. We drop all messages this produces. */
67 bool UnmountCdrom(string Path)
68 {
69 if (IsMounted(Path) == false)
70 return true;
71
72 for (int i=0;i<3;i++)
73 {
74
75 int Child = ExecFork();
76
77 // The child
78 if (Child == 0)
79 {
80 // Make all the fds /dev/null
81 for (int I = 0; I != 3; I++)
82 dup2(open("/dev/null",O_RDWR),I);
83
84 if (_config->Exists("Acquire::cdrom::"+Path+"::UMount") == true)
85 {
86 if (system(_config->Find("Acquire::cdrom::"+Path+"::UMount").c_str()) != 0)
87 _exit(100);
88 _exit(0);
89 }
90 else
91 {
92 const char *Args[10];
93 Args[0] = "umount";
94 Args[1] = Path.c_str();
95 Args[2] = 0;
96 execvp(Args[0],(char **)Args);
97 _exit(100);
98 }
99 }
100
101 // if it can not be umounted, give it a bit more time
102 // this can happen when auto-mount magic or fs/cdrom prober attack
103 if (ExecWait(Child,"umount",true) == true)
104 return true;
105 sleep(1);
106 }
107
108 return false;
109 }
110 /*}}}*/
111 // MountCdrom - Mount a cdrom /*{{{*/
112 // ---------------------------------------------------------------------
113 /* We fork mount and drop all messages */
114 bool MountCdrom(string Path, string DeviceName)
115 {
116 if (IsMounted(Path) == true)
117 return true;
118
119 int Child = ExecFork();
120
121 // The child
122 if (Child == 0)
123 {
124 // Make all the fds /dev/null
125 int null_fd = open("/dev/null",O_RDWR);
126 for (int I = 0; I != 3; I++)
127 dup2(null_fd, I);
128
129 if (_config->Exists("Acquire::cdrom::"+Path+"::Mount") == true)
130 {
131 if (system(_config->Find("Acquire::cdrom::"+Path+"::Mount").c_str()) != 0)
132 _exit(100);
133 _exit(0);
134 }
135 else
136 {
137 const char *Args[10];
138 Args[0] = "mount";
139 if (DeviceName == "")
140 {
141 Args[1] = Path.c_str();
142 Args[2] = 0;
143 } else {
144 Args[1] = DeviceName.c_str();
145 Args[2] = Path.c_str();
146 Args[3] = 0;
147 }
148 execvp(Args[0],(char **)Args);
149 _exit(100);
150 }
151 }
152
153 // Wait for mount
154 return ExecWait(Child,"mount",true);
155 }
156 /*}}}*/
157 // IdentCdrom - Generate a unique string for this CD /*{{{*/
158 // ---------------------------------------------------------------------
159 /* We convert everything we hash into a string, this prevents byte size/order
160 from effecting the outcome. */
161 bool IdentCdrom(string CD,string &Res,unsigned int Version)
162 {
163 MD5Summation Hash;
164 bool writable_media = false;
165
166 // if we are on a writable medium (like a usb-stick) that is just
167 // used like a cdrom don't use "." as it will constantly change,
168 // use .disk instead
169 if (access(CD.c_str(), W_OK) == 0 && DirectoryExists(CD+string("/.disk")))
170 {
171 writable_media = true;
172 CD = CD.append("/.disk");
173 if (_config->FindB("Debug::aptcdrom",false) == true)
174 std::clog << "Found writable cdrom, using alternative path: " << CD
175 << std::endl;
176 }
177
178 string StartDir = SafeGetCWD();
179 if (chdir(CD.c_str()) != 0)
180 return _error->Errno("chdir",_("Unable to change to %s"),CD.c_str());
181
182 DIR *D = opendir(".");
183 if (D == 0)
184 return _error->Errno("opendir",_("Unable to read %s"),CD.c_str());
185
186 /* Run over the directory, we assume that the reader order will never
187 change as the media is read-only. In theory if the kernel did
188 some sort of wacked caching this might not be true.. */
189 char S[300];
190 for (struct dirent *Dir = readdir(D); Dir != 0; Dir = readdir(D))
191 {
192 // Skip some files..
193 if (strcmp(Dir->d_name,".") == 0 ||
194 strcmp(Dir->d_name,"..") == 0)
195 continue;
196
197 if (Version <= 1)
198 {
199 sprintf(S,"%lu",(unsigned long)Dir->d_ino);
200 }
201 else
202 {
203 struct stat Buf;
204 if (stat(Dir->d_name,&Buf) != 0)
205 continue;
206 sprintf(S,"%lu",(unsigned long)Buf.st_mtime);
207 }
208
209 Hash.Add(S);
210 Hash.Add(Dir->d_name);
211 };
212
213 if (chdir(StartDir.c_str()) != 0) {
214 _error->Errno("chdir",_("Unable to change to %s"),StartDir.c_str());
215 closedir(D);
216 return false;
217 }
218 closedir(D);
219
220 // Some stats from the fsys
221 if (_config->FindB("Debug::identcdrom",false) == false)
222 {
223 struct statvfs Buf;
224 if (statvfs(CD.c_str(),&Buf) != 0)
225 return _error->Errno("statfs",_("Failed to stat the cdrom"));
226
227 // We use a kilobyte block size to advoid overflow
228 if (writable_media)
229 {
230 sprintf(S,"%lu",(long)(Buf.f_blocks*(Buf.f_bsize/1024)));
231 } else {
232 sprintf(S,"%lu %lu",(long)(Buf.f_blocks*(Buf.f_bsize/1024)),
233 (long)(Buf.f_bfree*(Buf.f_bsize/1024)));
234 }
235 Hash.Add(S);
236 sprintf(S,"-%u",Version);
237 }
238 else
239 sprintf(S,"-%u.debug",Version);
240
241 Res = Hash.Result().Value() + S;
242 return true;
243 }
244 /*}}}*/
245
246 // FindMountPointForDevice - Find mountpoint for the given device /*{{{*/
247 string FindMountPointForDevice(const char *devnode)
248 {
249 char buf[255];
250 char *out[10];
251 int i=0;
252
253 // this is the order that mount uses as well
254 const char *mount[] = { "/etc/mtab",
255 "/proc/mount",
256 NULL };
257
258 for (i=0; mount[i] != NULL; i++) {
259 if (FileExists(mount[i])) {
260 FILE *f=fopen(mount[i], "r");
261 while ( fgets(buf, sizeof(buf), f) != NULL) {
262 if (strncmp(buf, devnode, strlen(devnode)) == 0) {
263 if(TokSplitString(' ', buf, out, 10))
264 {
265 fclose(f);
266 // unescape the \0XXX chars in the path
267 string mount_point = out[1];
268 return DeEscapeString(mount_point);
269 }
270 }
271 }
272 fclose(f);
273 }
274 }
275
276 return string();
277 }
278 /*}}}*/