Branch data Line data Source code
1 : : /*
2 : : * libdpkg - Debian packaging suite library routines
3 : : * db-fsys-files.c - management of filesystem files database
4 : : *
5 : : * Copyright © 1995 Ian Jackson <ijackson@chiark.greenend.org.uk>
6 : : * Copyright © 2000,2001 Wichert Akkerman <wakkerma@debian.org>
7 : : * Copyright © 2008-2014 Guillem Jover <guillem@debian.org>
8 : : *
9 : : * This is free software; you can redistribute it and/or modify
10 : : * it under the terms of the GNU General Public License as published by
11 : : * the Free Software Foundation; either version 2 of the License, or
12 : : * (at your option) any later version.
13 : : *
14 : : * This is distributed in the hope that it will be useful,
15 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 : : * GNU General Public License for more details.
18 : : *
19 : : * You should have received a copy of the GNU General Public License
20 : : * along with this program. If not, see <https://www.gnu.org/licenses/>.
21 : : */
22 : :
23 : : #include <config.h>
24 : : #include <compat.h>
25 : :
26 : : #ifdef HAVE_LINUX_FIEMAP_H
27 : : #include <linux/fiemap.h>
28 : : #include <linux/fs.h>
29 : : #include <sys/ioctl.h>
30 : : #include <sys/vfs.h>
31 : : #endif
32 : : #include <sys/types.h>
33 : : #include <sys/stat.h>
34 : :
35 : : #include <errno.h>
36 : : #include <string.h>
37 : : #include <pwd.h>
38 : : #include <grp.h>
39 : : #include <fcntl.h>
40 : : #include <unistd.h>
41 : : #include <stdlib.h>
42 : :
43 : : #include <dpkg/i18n.h>
44 : : #include <dpkg/dpkg.h>
45 : : #include <dpkg/dpkg-db.h>
46 : : #include <dpkg/string.h>
47 : : #include <dpkg/path.h>
48 : : #include <dpkg/dir.h>
49 : : #include <dpkg/fdio.h>
50 : : #include <dpkg/pkg-array.h>
51 : : #include <dpkg/pkg-files.h>
52 : : #include <dpkg/progress.h>
53 : : #include <dpkg/db-ctrl.h>
54 : : #include <dpkg/db-fsys.h>
55 : :
56 : : /*** Generic data structures and routines. ***/
57 : :
58 : : static bool allpackagesdone = false;
59 : :
60 : 0 : void note_must_reread_files_inpackage(struct pkginfo *pkg) {
61 : 0 : allpackagesdone = false;
62 : 0 : pkg->files_list_valid = false;
63 : 0 : }
64 : :
65 : : enum pkg_filesdb_load_status {
66 : : PKG_FILESDB_LOAD_NONE = 0,
67 : : PKG_FILESDB_LOAD_INPROGRESS = 1,
68 : : PKG_FILESDB_LOAD_DONE = 2,
69 : : };
70 : :
71 : : static enum pkg_filesdb_load_status saidread = PKG_FILESDB_LOAD_NONE;
72 : :
73 : : static void
74 : 1 : fsys_list_parse_buffer(struct varbuf *vb, struct pkginfo *pkg)
75 : : {
76 : : struct fsys_namenode_list **files_tail;
77 : : char *loaded_list_end, *thisline;
78 : :
79 : 1 : loaded_list_end = vb->buf + vb->used;
80 : :
81 : 1 : files_tail = &pkg->files;
82 : 1 : thisline = vb->buf;
83 : :
84 [ + + ]: 2 : while (thisline < loaded_list_end) {
85 : : struct fsys_namenode *namenode;
86 : : char *nextline, *ptr;
87 : :
88 : 1 : ptr = memchr(thisline, '\n', loaded_list_end - thisline);
89 [ - + ]: 1 : if (ptr == NULL)
90 : 0 : ohshit(_("files list file for package '%.250s' is missing final newline"),
91 : : pkg_name(pkg, pnaw_nonambig));
92 : :
93 : : /* Where to start next time around. */
94 : 1 : nextline = ptr + 1;
95 : :
96 : : /* Strip trailing ‘/’. */
97 [ + - - + ]: 1 : if (ptr > thisline && ptr[-1] == '/')
98 : 0 : ptr--;
99 : :
100 : : /* Add the file to the list. */
101 [ - + ]: 1 : if (ptr == thisline)
102 : 0 : ohshit(_("files list file for package '%.250s' contains empty filename"),
103 : : pkg_name(pkg, pnaw_nonambig));
104 : 1 : *ptr = '\0';
105 : :
106 : 1 : namenode = fsys_hash_find_node(thisline, FHFF_NONE);
107 : 1 : files_tail = pkg_files_add_file(pkg, namenode, files_tail);
108 : :
109 : 1 : thisline = nextline;
110 : : }
111 : 1 : }
112 : :
113 : : /**
114 : : * Load the list of files in this package into memory, or update the
115 : : * list if it is there but stale.
116 : : */
117 : : void
118 : 1 : ensure_packagefiles_available(struct pkginfo *pkg)
119 : : {
120 : : const char *filelistfile;
121 : 1 : struct varbuf buf = VARBUF_INIT;
122 : 1 : struct dpkg_error err = DPKG_ERROR_INIT;
123 : :
124 [ - + ]: 1 : if (pkg->files_list_valid)
125 : 0 : return;
126 : :
127 : : /* Throw away any stale data, if there was any. */
128 : 1 : pkg_files_blank(pkg);
129 : :
130 : : /* Packages which aren't installed don't have a files list. */
131 [ - + ]: 1 : if (pkg->status == PKG_STAT_NOTINSTALLED) {
132 : 0 : pkg->files_list_valid = true;
133 : 0 : return;
134 : : }
135 : :
136 : 1 : filelistfile = pkg_infodb_get_file(pkg, &pkg->installed, LISTFILE);
137 : :
138 : 1 : onerr_abort++;
139 : :
140 [ - + ]: 1 : if (file_slurp(filelistfile, &buf, &err) < 0) {
141 [ # # ]: 0 : if (err.syserrno != ENOENT)
142 : 0 : dpkg_error_print(&err, _("loading files list file for package '%s'"),
143 : : pkg_name(pkg, pnaw_nonambig));
144 : :
145 : 0 : onerr_abort--;
146 [ # # # # ]: 0 : if (pkg->status != PKG_STAT_CONFIGFILES &&
147 : 0 : dpkg_version_is_informative(&pkg->configversion)) {
148 : 0 : warning(_("files list file for package '%.250s' missing; assuming "
149 : : "package has no files currently installed"),
150 : : pkg_name(pkg, pnaw_nonambig));
151 : : }
152 : 0 : pkg->files = NULL;
153 : 0 : pkg->files_list_valid = true;
154 : 0 : return;
155 : : }
156 : :
157 [ + - ]: 1 : if (buf.used)
158 : 1 : fsys_list_parse_buffer(&buf, pkg);
159 : :
160 : 1 : varbuf_destroy(&buf);
161 : :
162 : 1 : onerr_abort--;
163 : :
164 : 1 : pkg->files_list_valid = true;
165 : : }
166 : :
167 : : #if defined(HAVE_LINUX_FIEMAP_H)
168 : : static int
169 : 0 : pkg_sorter_by_files_list_phys_offs(const void *a, const void *b)
170 : : {
171 : 0 : const struct pkginfo *pa = *(const struct pkginfo **)a;
172 : 0 : const struct pkginfo *pb = *(const struct pkginfo **)b;
173 : :
174 : : /* We can't simply subtract, because the difference may be greater than
175 : : * INT_MAX. */
176 [ # # ]: 0 : if (pa->files_list_phys_offs < pb->files_list_phys_offs)
177 : 0 : return -1;
178 [ # # ]: 0 : else if (pa->files_list_phys_offs > pb->files_list_phys_offs)
179 : 0 : return 1;
180 : : else
181 : 0 : return 0;
182 : : }
183 : :
184 : : #define DPKG_FIEMAP_EXTENTS 1
185 : : #define DPKG_FIEMAP_BUF_SIZE \
186 : : (sizeof(struct fiemap) + \
187 : : sizeof(struct fiemap_extent) * DPKG_FIEMAP_EXTENTS) / sizeof(__u64)
188 : :
189 : : static void
190 : 0 : pkg_files_optimize_load(struct pkg_array *array)
191 : : {
192 : : __u64 fiemap_buf[DPKG_FIEMAP_BUF_SIZE];
193 : 0 : struct fiemap *fm = (struct fiemap *)fiemap_buf;
194 : : struct statfs fs;
195 : : int i;
196 : :
197 : : /* Get the filesystem block size. */
198 [ # # ]: 0 : if (statfs(pkg_infodb_get_dir(), &fs) < 0)
199 : 0 : return;
200 : :
201 : : /* Sort packages by the physical location of their list files, so that
202 : : * scanning them later will minimize disk drive head movements. */
203 [ # # ]: 0 : for (i = 0; i < array->n_pkgs; i++) {
204 : 0 : struct pkginfo *pkg = array->pkgs[i];
205 : : const char *listfile;
206 : : int fd;
207 : :
208 [ # # ]: 0 : if (pkg->status == PKG_STAT_NOTINSTALLED ||
209 [ # # ]: 0 : pkg->files_list_phys_offs != 0)
210 : 0 : continue;
211 : :
212 : 0 : pkg->files_list_phys_offs = -1;
213 : :
214 : 0 : listfile = pkg_infodb_get_file(pkg, &pkg->installed, LISTFILE);
215 : :
216 : 0 : fd = open(listfile, O_RDONLY);
217 [ # # ]: 0 : if (fd < 0)
218 : 0 : continue;
219 : :
220 : 0 : memset(fiemap_buf, 0, sizeof(fiemap_buf));
221 : 0 : fm->fm_start = 0;
222 : 0 : fm->fm_length = fs.f_bsize;
223 : 0 : fm->fm_flags = 0;
224 : 0 : fm->fm_extent_count = DPKG_FIEMAP_EXTENTS;
225 : :
226 [ # # ]: 0 : if (ioctl(fd, FS_IOC_FIEMAP, (unsigned long)fm) == 0)
227 : 0 : pkg->files_list_phys_offs = fm->fm_extents[0].fe_physical;
228 : :
229 : 0 : close(fd);
230 : : }
231 : :
232 : 0 : pkg_array_sort(array, pkg_sorter_by_files_list_phys_offs);
233 : : }
234 : : #elif defined(HAVE_POSIX_FADVISE)
235 : : static void
236 : : pkg_files_optimize_load(struct pkg_array *array)
237 : : {
238 : : int i;
239 : :
240 : : /* Ask the kernel to start preloading the list files, so as to get a
241 : : * boost when later we actually load them. */
242 : : for (i = 0; i < array->n_pkgs; i++) {
243 : : struct pkginfo *pkg = array->pkgs[i];
244 : : const char *listfile;
245 : : int fd;
246 : :
247 : : listfile = pkg_infodb_get_file(pkg, &pkg->installed, LISTFILE);
248 : :
249 : : fd = open(listfile, O_RDONLY | O_NONBLOCK);
250 : : if (fd >= 0) {
251 : : posix_fadvise(fd, 0, 0, POSIX_FADV_WILLNEED);
252 : : close(fd);
253 : : }
254 : : }
255 : : }
256 : : #else
257 : : static void
258 : : pkg_files_optimize_load(struct pkg_array *array)
259 : : {
260 : : }
261 : : #endif
262 : :
263 : 0 : void ensure_allinstfiles_available(void) {
264 : : struct pkg_array array;
265 : : struct progress progress;
266 : : int i;
267 : :
268 [ # # ]: 0 : if (allpackagesdone) return;
269 [ # # ]: 0 : if (saidread < PKG_FILESDB_LOAD_DONE) {
270 : 0 : int max = pkg_hash_count_pkg();
271 : :
272 : 0 : saidread = PKG_FILESDB_LOAD_INPROGRESS;
273 : 0 : progress_init(&progress, _("(Reading database ... "), max);
274 : : }
275 : :
276 : 0 : pkg_array_init_from_hash(&array);
277 : :
278 : 0 : pkg_files_optimize_load(&array);
279 : :
280 [ # # ]: 0 : for (i = 0; i < array.n_pkgs; i++) {
281 : 0 : struct pkginfo *pkg = array.pkgs[i];
282 : :
283 : 0 : ensure_packagefiles_available(pkg);
284 : :
285 [ # # ]: 0 : if (saidread == PKG_FILESDB_LOAD_INPROGRESS)
286 : 0 : progress_step(&progress);
287 : : }
288 : :
289 : 0 : pkg_array_destroy(&array);
290 : :
291 : 0 : allpackagesdone = true;
292 : :
293 [ # # ]: 0 : if (saidread == PKG_FILESDB_LOAD_INPROGRESS) {
294 : 0 : progress_done(&progress);
295 : 0 : printf(P_("%d file or directory currently installed.)\n",
296 : : "%d files and directories currently installed.)\n",
297 : : fsys_hash_entries()),
298 : : fsys_hash_entries());
299 : 0 : saidread = PKG_FILESDB_LOAD_DONE;
300 : : }
301 : : }
302 : :
303 : 0 : void ensure_allinstfiles_available_quiet(void) {
304 : 0 : saidread = PKG_FILESDB_LOAD_DONE;
305 : 0 : ensure_allinstfiles_available();
306 : 0 : }
307 : :
308 : : /*
309 : : * If mask is nonzero, will not write any file whose fsys_namenode
310 : : * has any flag bits set in mask.
311 : : */
312 : : void
313 : 0 : write_filelist_except(struct pkginfo *pkg, struct pkgbin *pkgbin,
314 : : struct fsys_namenode_list *list, enum fsys_namenode_flags mask)
315 : : {
316 : : struct atomic_file *file;
317 : : struct fsys_namenode_list *node;
318 : : const char *listfile;
319 : :
320 : 0 : listfile = pkg_infodb_get_file(pkg, pkgbin, LISTFILE);
321 : :
322 : 0 : file = atomic_file_new(listfile, 0);
323 : 0 : atomic_file_open(file);
324 : :
325 [ # # ]: 0 : for (node = list; node; node = node->next) {
326 [ # # # # ]: 0 : if (!(mask && (node->namenode->flags & mask))) {
327 : 0 : fputs(node->namenode->name, file->fp);
328 : 0 : putc('\n', file->fp);
329 : : }
330 : : }
331 : :
332 : 0 : atomic_file_sync(file);
333 : 0 : atomic_file_close(file);
334 : 0 : atomic_file_commit(file);
335 : 0 : atomic_file_free(file);
336 : :
337 : 0 : dir_sync_path(pkg_infodb_get_dir());
338 : :
339 : 0 : note_must_reread_files_inpackage(pkg);
340 : 0 : }
|