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 : : /**
74 : : * Load the list of files in this package into memory, or update the
75 : : * list if it is there but stale.
76 : : */
77 : : void
78 : 1 : ensure_packagefiles_available(struct pkginfo *pkg)
79 : : {
80 : : const char *filelistfile;
81 : : struct fsys_namenode_list **lendp;
82 : : char *loaded_list_end, *thisline, *nextline, *ptr;
83 : 1 : struct varbuf buf = VARBUF_INIT;
84 : 1 : struct dpkg_error err = DPKG_ERROR_INIT;
85 : :
86 [ - + ]: 1 : if (pkg->files_list_valid)
87 : 0 : return;
88 : :
89 : : /* Throw away any stale data, if there was any. */
90 : 1 : pkg_files_blank(pkg);
91 : :
92 : : /* Packages which aren't installed don't have a files list. */
93 [ - + ]: 1 : if (pkg->status == PKG_STAT_NOTINSTALLED) {
94 : 0 : pkg->files_list_valid = true;
95 : 0 : return;
96 : : }
97 : :
98 : 1 : filelistfile = pkg_infodb_get_file(pkg, &pkg->installed, LISTFILE);
99 : :
100 : 1 : onerr_abort++;
101 : :
102 [ - + ]: 1 : if (file_slurp(filelistfile, &buf, &err) < 0) {
103 [ # # ]: 0 : if (err.syserrno != ENOENT)
104 : 0 : dpkg_error_print(&err, _("loading files list file for package '%s'"),
105 : : pkg_name(pkg, pnaw_nonambig));
106 : :
107 : 0 : onerr_abort--;
108 [ # # # # ]: 0 : if (pkg->status != PKG_STAT_CONFIGFILES &&
109 : 0 : dpkg_version_is_informative(&pkg->configversion)) {
110 : 0 : warning(_("files list file for package '%.250s' missing; assuming "
111 : : "package has no files currently installed"),
112 : : pkg_name(pkg, pnaw_nonambig));
113 : : }
114 : 0 : pkg->files = NULL;
115 : 0 : pkg->files_list_valid = true;
116 : 0 : return;
117 : : }
118 : :
119 [ + - ]: 1 : if (buf.used) {
120 : 1 : loaded_list_end = buf.buf + buf.used;
121 : :
122 : 1 : lendp = &pkg->files;
123 : 1 : thisline = buf.buf;
124 [ + + ]: 2 : while (thisline < loaded_list_end) {
125 : : struct fsys_namenode *namenode;
126 : :
127 : 1 : ptr = memchr(thisline, '\n', loaded_list_end - thisline);
128 [ - + ]: 1 : if (ptr == NULL)
129 : 0 : ohshit(_("files list file for package '%.250s' is missing final newline"),
130 : : pkg_name(pkg, pnaw_nonambig));
131 : : /* Where to start next time around. */
132 : 1 : nextline = ptr + 1;
133 : : /* Strip trailing ‘/’. */
134 [ + - - + ]: 1 : if (ptr > thisline && ptr[-1] == '/') ptr--;
135 : : /* Add the file to the list. */
136 [ - + ]: 1 : if (ptr == thisline)
137 : 0 : ohshit(_("files list file for package '%.250s' contains empty filename"),
138 : : pkg_name(pkg, pnaw_nonambig));
139 : 1 : *ptr = '\0';
140 : :
141 : 1 : namenode = fsys_hash_find_node(thisline, 0);
142 : 1 : lendp = pkg_files_add_file(pkg, namenode, lendp);
143 : 1 : thisline = nextline;
144 : : }
145 : : }
146 : :
147 : 1 : varbuf_destroy(&buf);
148 : :
149 : 1 : onerr_abort--;
150 : :
151 : 1 : pkg->files_list_valid = true;
152 : : }
153 : :
154 : : #if defined(HAVE_LINUX_FIEMAP_H)
155 : : static int
156 : 0 : pkg_sorter_by_files_list_phys_offs(const void *a, const void *b)
157 : : {
158 : 0 : const struct pkginfo *pa = *(const struct pkginfo **)a;
159 : 0 : const struct pkginfo *pb = *(const struct pkginfo **)b;
160 : :
161 : : /* We can't simply subtract, because the difference may be greater than
162 : : * INT_MAX. */
163 [ # # ]: 0 : if (pa->files_list_phys_offs < pb->files_list_phys_offs)
164 : 0 : return -1;
165 [ # # ]: 0 : else if (pa->files_list_phys_offs > pb->files_list_phys_offs)
166 : 0 : return 1;
167 : : else
168 : 0 : return 0;
169 : : }
170 : :
171 : : #define DPKG_FIEMAP_EXTENTS 1
172 : : #define DPKG_FIEMAP_BUF_SIZE \
173 : : (sizeof(struct fiemap) + \
174 : : sizeof(struct fiemap_extent) * DPKG_FIEMAP_EXTENTS) / sizeof(__u64)
175 : :
176 : : static void
177 : 0 : pkg_files_optimize_load(struct pkg_array *array)
178 : : {
179 : : __u64 fiemap_buf[DPKG_FIEMAP_BUF_SIZE];
180 : 0 : struct fiemap *fm = (struct fiemap *)fiemap_buf;
181 : : struct statfs fs;
182 : : int i;
183 : :
184 : : /* Get the filesystem block size. */
185 [ # # ]: 0 : if (statfs(pkg_infodb_get_dir(), &fs) < 0)
186 : 0 : return;
187 : :
188 : : /* Sort packages by the physical location of their list files, so that
189 : : * scanning them later will minimize disk drive head movements. */
190 [ # # ]: 0 : for (i = 0; i < array->n_pkgs; i++) {
191 : 0 : struct pkginfo *pkg = array->pkgs[i];
192 : : const char *listfile;
193 : : int fd;
194 : :
195 [ # # ]: 0 : if (pkg->status == PKG_STAT_NOTINSTALLED ||
196 [ # # ]: 0 : pkg->files_list_phys_offs != 0)
197 : 0 : continue;
198 : :
199 : 0 : pkg->files_list_phys_offs = -1;
200 : :
201 : 0 : listfile = pkg_infodb_get_file(pkg, &pkg->installed, LISTFILE);
202 : :
203 : 0 : fd = open(listfile, O_RDONLY);
204 [ # # ]: 0 : if (fd < 0)
205 : 0 : continue;
206 : :
207 : 0 : memset(fiemap_buf, 0, sizeof(fiemap_buf));
208 : 0 : fm->fm_start = 0;
209 : 0 : fm->fm_length = fs.f_bsize;
210 : 0 : fm->fm_flags = 0;
211 : 0 : fm->fm_extent_count = DPKG_FIEMAP_EXTENTS;
212 : :
213 [ # # ]: 0 : if (ioctl(fd, FS_IOC_FIEMAP, (unsigned long)fm) == 0)
214 : 0 : pkg->files_list_phys_offs = fm->fm_extents[0].fe_physical;
215 : :
216 : 0 : close(fd);
217 : : }
218 : :
219 : 0 : pkg_array_sort(array, pkg_sorter_by_files_list_phys_offs);
220 : : }
221 : : #elif defined(HAVE_POSIX_FADVISE)
222 : : static void
223 : : pkg_files_optimize_load(struct pkg_array *array)
224 : : {
225 : : int i;
226 : :
227 : : /* Ask the kernel to start preloading the list files, so as to get a
228 : : * boost when later we actually load them. */
229 : : for (i = 0; i < array->n_pkgs; i++) {
230 : : struct pkginfo *pkg = array->pkgs[i];
231 : : const char *listfile;
232 : : int fd;
233 : :
234 : : listfile = pkg_infodb_get_file(pkg, &pkg->installed, LISTFILE);
235 : :
236 : : fd = open(listfile, O_RDONLY | O_NONBLOCK);
237 : : if (fd != -1) {
238 : : posix_fadvise(fd, 0, 0, POSIX_FADV_WILLNEED);
239 : : close(fd);
240 : : }
241 : : }
242 : : }
243 : : #else
244 : : static void
245 : : pkg_files_optimize_load(struct pkg_array *array)
246 : : {
247 : : }
248 : : #endif
249 : :
250 : 0 : void ensure_allinstfiles_available(void) {
251 : : struct pkg_array array;
252 : : struct pkginfo *pkg;
253 : : struct progress progress;
254 : : int i;
255 : :
256 [ # # ]: 0 : if (allpackagesdone) return;
257 [ # # ]: 0 : if (saidread < PKG_FILESDB_LOAD_DONE) {
258 : 0 : int max = pkg_hash_count_pkg();
259 : :
260 : 0 : saidread = PKG_FILESDB_LOAD_INPROGRESS;
261 : 0 : progress_init(&progress, _("(Reading database ... "), max);
262 : : }
263 : :
264 : 0 : pkg_array_init_from_hash(&array);
265 : :
266 : 0 : pkg_files_optimize_load(&array);
267 : :
268 [ # # ]: 0 : for (i = 0; i < array.n_pkgs; i++) {
269 : 0 : pkg = array.pkgs[i];
270 : 0 : ensure_packagefiles_available(pkg);
271 : :
272 [ # # ]: 0 : if (saidread == PKG_FILESDB_LOAD_INPROGRESS)
273 : 0 : progress_step(&progress);
274 : : }
275 : :
276 : 0 : pkg_array_destroy(&array);
277 : :
278 : 0 : allpackagesdone = true;
279 : :
280 [ # # ]: 0 : if (saidread == PKG_FILESDB_LOAD_INPROGRESS) {
281 : 0 : progress_done(&progress);
282 : 0 : printf(P_("%d file or directory currently installed.)\n",
283 : : "%d files and directories currently installed.)\n",
284 : : fsys_hash_entries()),
285 : : fsys_hash_entries());
286 : 0 : saidread = PKG_FILESDB_LOAD_DONE;
287 : : }
288 : : }
289 : :
290 : 0 : void ensure_allinstfiles_available_quiet(void) {
291 : 0 : saidread = PKG_FILESDB_LOAD_DONE;
292 : 0 : ensure_allinstfiles_available();
293 : 0 : }
294 : :
295 : : /*
296 : : * If mask is nonzero, will not write any file whose fsys_namenode
297 : : * has any flag bits set in mask.
298 : : */
299 : : void
300 : 0 : write_filelist_except(struct pkginfo *pkg, struct pkgbin *pkgbin,
301 : : struct fsys_namenode_list *list, enum fsys_namenode_flags mask)
302 : : {
303 : : struct atomic_file *file;
304 : : struct fsys_namenode_list *node;
305 : : const char *listfile;
306 : :
307 : 0 : listfile = pkg_infodb_get_file(pkg, pkgbin, LISTFILE);
308 : :
309 : 0 : file = atomic_file_new(listfile, 0);
310 : 0 : atomic_file_open(file);
311 : :
312 [ # # ]: 0 : for (node = list; node; node = node->next) {
313 [ # # # # ]: 0 : if (!(mask && (node->namenode->flags & mask))) {
314 : 0 : fputs(node->namenode->name, file->fp);
315 : 0 : putc('\n', file->fp);
316 : : }
317 : : }
318 : :
319 : 0 : atomic_file_sync(file);
320 : 0 : atomic_file_close(file);
321 : 0 : atomic_file_commit(file);
322 : 0 : atomic_file_free(file);
323 : :
324 : 0 : dir_sync_path(pkg_infodb_get_dir());
325 : :
326 : 0 : note_must_reread_files_inpackage(pkg);
327 : 0 : }
|