Commit | Line | Data |
---|---|---|
1479465f GJ |
1 | /* |
2 | * dpkg - main program for package management | |
3 | * filesdb.c - management of database of files installed on system | |
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 <assert.h> | |
36 | #include <errno.h> | |
37 | #include <string.h> | |
38 | #include <pwd.h> | |
39 | #include <grp.h> | |
40 | #include <fcntl.h> | |
41 | #include <unistd.h> | |
42 | #include <stdlib.h> | |
43 | ||
44 | #include <dpkg/i18n.h> | |
45 | #include <dpkg/dpkg.h> | |
46 | #include <dpkg/dpkg-db.h> | |
47 | #include <dpkg/string.h> | |
48 | #include <dpkg/path.h> | |
49 | #include <dpkg/dir.h> | |
50 | #include <dpkg/fdio.h> | |
51 | #include <dpkg/pkg-array.h> | |
52 | #include <dpkg/progress.h> | |
53 | ||
54 | #include "filesdb.h" | |
55 | #include "infodb.h" | |
56 | #include "main.h" | |
57 | ||
58 | /*** filepackages support for tracking packages owning a file. ***/ | |
59 | ||
60 | struct filepackages_iterator { | |
61 | struct pkg_list *pkg_node; | |
62 | }; | |
63 | ||
64 | struct filepackages_iterator * | |
65 | filepackages_iter_new(struct filenamenode *fnn) | |
66 | { | |
67 | struct filepackages_iterator *iter; | |
68 | ||
69 | iter = m_malloc(sizeof(*iter)); | |
70 | iter->pkg_node = fnn->packages; | |
71 | ||
72 | return iter; | |
73 | } | |
74 | ||
75 | struct pkginfo * | |
76 | filepackages_iter_next(struct filepackages_iterator *iter) | |
77 | { | |
78 | struct pkg_list *pkg_node; | |
79 | ||
80 | if (iter->pkg_node == NULL) | |
81 | return NULL; | |
82 | ||
83 | pkg_node = iter->pkg_node; | |
84 | iter->pkg_node = pkg_node->next; | |
85 | ||
86 | return pkg_node->pkg; | |
87 | } | |
88 | ||
89 | void | |
90 | filepackages_iter_free(struct filepackages_iterator *iter) | |
91 | { | |
92 | free(iter); | |
93 | } | |
94 | ||
95 | /*** Generic data structures and routines. ***/ | |
96 | ||
97 | static bool allpackagesdone = false; | |
98 | static int nfiles= 0; | |
99 | ||
100 | void | |
101 | ensure_package_clientdata(struct pkginfo *pkg) | |
102 | { | |
103 | if (pkg->clientdata) | |
104 | return; | |
105 | pkg->clientdata = nfmalloc(sizeof(struct perpackagestate)); | |
106 | pkg->clientdata->istobe = PKG_ISTOBE_NORMAL; | |
107 | pkg->clientdata->color = PKG_CYCLE_WHITE; | |
108 | pkg->clientdata->enqueued = false; | |
109 | pkg->clientdata->fileslistvalid = false; | |
110 | pkg->clientdata->files = NULL; | |
111 | pkg->clientdata->replacingfilesandsaid = 0; | |
112 | pkg->clientdata->cmdline_seen = 0; | |
113 | pkg->clientdata->listfile_phys_offs = 0; | |
114 | pkg->clientdata->trigprocdeferred = NULL; | |
115 | } | |
116 | ||
117 | void note_must_reread_files_inpackage(struct pkginfo *pkg) { | |
118 | allpackagesdone = false; | |
119 | ensure_package_clientdata(pkg); | |
120 | pkg->clientdata->fileslistvalid = false; | |
121 | } | |
122 | ||
123 | enum pkg_filesdb_load_status { | |
124 | PKG_FILESDB_LOAD_NONE = 0, | |
125 | PKG_FILESDB_LOAD_INPROGRESS = 1, | |
126 | PKG_FILESDB_LOAD_DONE = 2, | |
127 | }; | |
128 | ||
129 | static enum pkg_filesdb_load_status saidread = PKG_FILESDB_LOAD_NONE; | |
130 | ||
131 | /** | |
132 | * Erase the files saved in pkg. | |
133 | */ | |
134 | static void | |
135 | pkg_files_blank(struct pkginfo *pkg) | |
136 | { | |
137 | struct fileinlist *current; | |
138 | ||
139 | /* Anything to empty? */ | |
140 | if (!pkg->clientdata) | |
141 | return; | |
142 | ||
143 | for (current= pkg->clientdata->files; | |
144 | current; | |
145 | current= current->next) { | |
146 | struct pkg_list *pkg_node, *pkg_prev = NULL; | |
147 | ||
148 | /* For each file that used to be in the package, | |
149 | * go through looking for this package's entry in the list | |
150 | * of packages containing this file, and blank it out. */ | |
151 | for (pkg_node = current->namenode->packages; | |
152 | pkg_node; | |
153 | pkg_node = pkg_node->next) { | |
154 | if (pkg_node->pkg == pkg) { | |
155 | if (pkg_prev) | |
156 | pkg_prev->next = pkg_node->next; | |
157 | else | |
158 | current->namenode->packages = pkg_node->next; | |
159 | ||
160 | /* The actual filelist links were allocated using nfmalloc, so | |
161 | * we shouldn't free them. */ | |
162 | break; | |
163 | } | |
164 | pkg_prev = pkg_node; | |
165 | } | |
166 | } | |
167 | pkg->clientdata->files = NULL; | |
168 | } | |
169 | ||
170 | static struct fileinlist ** | |
171 | pkg_files_add_file(struct pkginfo *pkg, struct filenamenode *namenode, | |
172 | struct fileinlist **file_tail) | |
173 | { | |
174 | struct fileinlist *newent; | |
175 | struct pkg_list *pkg_node; | |
176 | ||
177 | ensure_package_clientdata(pkg); | |
178 | ||
179 | if (file_tail == NULL) | |
180 | file_tail = &pkg->clientdata->files; | |
181 | ||
182 | /* Make sure we're at the end. */ | |
183 | while ((*file_tail) != NULL) { | |
184 | file_tail = &((*file_tail)->next); | |
185 | } | |
186 | ||
187 | /* Create a new node. */ | |
188 | newent = nfmalloc(sizeof(struct fileinlist)); | |
189 | newent->namenode = namenode; | |
190 | newent->next = NULL; | |
191 | *file_tail = newent; | |
192 | file_tail = &newent->next; | |
193 | ||
194 | /* Add pkg to newent's package list. */ | |
195 | pkg_node = nfmalloc(sizeof(*pkg_node)); | |
196 | pkg_node->pkg = pkg; | |
197 | pkg_node->next = newent->namenode->packages; | |
198 | newent->namenode->packages = pkg_node; | |
199 | ||
200 | /* Return the position for the next guy. */ | |
201 | return file_tail; | |
202 | } | |
203 | ||
204 | /** | |
205 | * Load the list of files in this package into memory, or update the | |
206 | * list if it is there but stale. | |
207 | */ | |
208 | void | |
209 | ensure_packagefiles_available(struct pkginfo *pkg) | |
210 | { | |
211 | static int fd; | |
212 | const char *filelistfile; | |
213 | struct fileinlist **lendp; | |
214 | struct stat stat_buf; | |
215 | char *loaded_list, *loaded_list_end, *thisline, *nextline, *ptr; | |
216 | ||
217 | if (pkg->clientdata && pkg->clientdata->fileslistvalid) | |
218 | return; | |
219 | ensure_package_clientdata(pkg); | |
220 | ||
221 | /* Throw away any stale data, if there was any. */ | |
222 | pkg_files_blank(pkg); | |
223 | ||
224 | /* Packages which aren't installed don't have a files list. */ | |
225 | if (pkg->status == PKG_STAT_NOTINSTALLED) { | |
226 | pkg->clientdata->fileslistvalid = true; | |
227 | return; | |
228 | } | |
229 | ||
230 | filelistfile = pkg_infodb_get_file(pkg, &pkg->installed, LISTFILE); | |
231 | ||
232 | onerr_abort++; | |
233 | ||
234 | fd= open(filelistfile,O_RDONLY); | |
235 | ||
236 | if (fd==-1) { | |
237 | if (errno != ENOENT) | |
238 | ohshite(_("unable to open files list file for package '%.250s'"), | |
239 | pkg_name(pkg, pnaw_nonambig)); | |
240 | onerr_abort--; | |
241 | if (pkg->status != PKG_STAT_CONFIGFILES && | |
242 | dpkg_version_is_informative(&pkg->configversion)) { | |
243 | warning(_("files list file for package '%.250s' missing; assuming " | |
244 | "package has no files currently installed"), | |
245 | pkg_name(pkg, pnaw_nonambig)); | |
246 | } | |
247 | pkg->clientdata->files = NULL; | |
248 | pkg->clientdata->fileslistvalid = true; | |
249 | return; | |
250 | } | |
251 | ||
252 | push_cleanup(cu_closefd, ehflag_bombout, NULL, 0, 1, &fd); | |
253 | ||
254 | if (fstat(fd, &stat_buf)) | |
255 | ohshite(_("unable to stat files list file for package '%.250s'"), | |
256 | pkg_name(pkg, pnaw_nonambig)); | |
257 | ||
258 | if (!S_ISREG(stat_buf.st_mode)) | |
259 | ohshit(_("files list for package '%.250s' is not a regular file"), | |
260 | pkg_name(pkg, pnaw_nonambig)); | |
261 | ||
262 | if (stat_buf.st_size) { | |
263 | loaded_list = nfmalloc(stat_buf.st_size); | |
264 | loaded_list_end = loaded_list + stat_buf.st_size; | |
265 | ||
266 | if (fd_read(fd, loaded_list, stat_buf.st_size) < 0) | |
267 | ohshite(_("reading files list for package '%.250s'"), | |
268 | pkg_name(pkg, pnaw_nonambig)); | |
269 | ||
270 | lendp= &pkg->clientdata->files; | |
271 | thisline = loaded_list; | |
272 | while (thisline < loaded_list_end) { | |
273 | struct filenamenode *namenode; | |
274 | ||
275 | ptr = memchr(thisline, '\n', loaded_list_end - thisline); | |
276 | if (ptr == NULL) | |
277 | ohshit(_("files list file for package '%.250s' is missing final newline"), | |
278 | pkg_name(pkg, pnaw_nonambig)); | |
279 | /* Where to start next time around. */ | |
280 | nextline = ptr + 1; | |
281 | /* Strip trailing ‘/’. */ | |
282 | if (ptr > thisline && ptr[-1] == '/') ptr--; | |
283 | /* Add the file to the list. */ | |
284 | if (ptr == thisline) | |
285 | ohshit(_("files list file for package '%.250s' contains empty filename"), | |
286 | pkg_name(pkg, pnaw_nonambig)); | |
287 | *ptr = '\0'; | |
288 | ||
289 | namenode = findnamenode(thisline, fnn_nocopy); | |
290 | lendp = pkg_files_add_file(pkg, namenode, lendp); | |
291 | thisline = nextline; | |
292 | } | |
293 | } | |
294 | pop_cleanup(ehflag_normaltidy); /* fd = open() */ | |
295 | if (close(fd)) | |
296 | ohshite(_("error closing files list file for package '%.250s'"), | |
297 | pkg_name(pkg, pnaw_nonambig)); | |
298 | ||
299 | onerr_abort--; | |
300 | ||
301 | pkg->clientdata->fileslistvalid = true; | |
302 | } | |
303 | ||
304 | #if defined(HAVE_LINUX_FIEMAP_H) | |
305 | static int | |
306 | pkg_sorter_by_listfile_phys_offs(const void *a, const void *b) | |
307 | { | |
308 | const struct pkginfo *pa = *(const struct pkginfo **)a; | |
309 | const struct pkginfo *pb = *(const struct pkginfo **)b; | |
310 | ||
311 | /* We can't simply subtract, because the difference may be greater than | |
312 | * INT_MAX. */ | |
313 | if (pa->clientdata->listfile_phys_offs < pb->clientdata->listfile_phys_offs) | |
314 | return -1; | |
315 | else if (pa->clientdata->listfile_phys_offs > pb->clientdata->listfile_phys_offs) | |
316 | return 1; | |
317 | else | |
318 | return 0; | |
319 | } | |
320 | ||
321 | static void | |
322 | pkg_files_optimize_load(struct pkg_array *array) | |
323 | { | |
324 | struct statfs fs; | |
325 | int i; | |
326 | ||
327 | /* Get the filesystem block size. */ | |
328 | if (statfs(pkg_infodb_get_dir(), &fs) < 0) | |
329 | return; | |
330 | ||
331 | /* Sort packages by the physical location of their list files, so that | |
332 | * scanning them later will minimize disk drive head movements. */ | |
333 | for (i = 0; i < array->n_pkgs; i++) { | |
334 | struct pkginfo *pkg = array->pkgs[i]; | |
335 | struct { | |
336 | struct fiemap fiemap; | |
337 | struct fiemap_extent extent; | |
338 | } fm; | |
339 | const char *listfile; | |
340 | int fd; | |
341 | ||
342 | ensure_package_clientdata(pkg); | |
343 | ||
344 | if (pkg->status == PKG_STAT_NOTINSTALLED || | |
345 | pkg->clientdata->listfile_phys_offs != 0) | |
346 | continue; | |
347 | ||
348 | pkg->clientdata->listfile_phys_offs = -1; | |
349 | ||
350 | listfile = pkg_infodb_get_file(pkg, &pkg->installed, LISTFILE); | |
351 | ||
352 | fd = open(listfile, O_RDONLY); | |
353 | if (fd < 0) | |
354 | continue; | |
355 | ||
356 | memset(&fm, 0, sizeof(fm)); | |
357 | fm.fiemap.fm_start = 0; | |
358 | fm.fiemap.fm_length = fs.f_bsize; | |
359 | fm.fiemap.fm_flags = 0; | |
360 | fm.fiemap.fm_extent_count = 1; | |
361 | ||
362 | if (ioctl(fd, FS_IOC_FIEMAP, (unsigned long)&fm) == 0) | |
363 | pkg->clientdata->listfile_phys_offs = fm.fiemap.fm_extents[0].fe_physical; | |
364 | ||
365 | close(fd); | |
366 | } | |
367 | ||
368 | pkg_array_sort(array, pkg_sorter_by_listfile_phys_offs); | |
369 | } | |
370 | #elif defined(HAVE_POSIX_FADVISE) | |
371 | static void | |
372 | pkg_files_optimize_load(struct pkg_array *array) | |
373 | { | |
374 | int i; | |
375 | ||
376 | /* Ask the kernel to start preloading the list files, so as to get a | |
377 | * boost when later we actually load them. */ | |
378 | for (i = 0; i < array->n_pkgs; i++) { | |
379 | struct pkginfo *pkg = array->pkgs[i]; | |
380 | const char *listfile; | |
381 | int fd; | |
382 | ||
383 | listfile = pkg_infodb_get_file(pkg, &pkg->installed, LISTFILE); | |
384 | ||
385 | fd = open(listfile, O_RDONLY | O_NONBLOCK); | |
386 | if (fd != -1) { | |
387 | posix_fadvise(fd, 0, 0, POSIX_FADV_WILLNEED); | |
388 | close(fd); | |
389 | } | |
390 | } | |
391 | } | |
392 | #else | |
393 | static void | |
394 | pkg_files_optimize_load(struct pkg_array *array) | |
395 | { | |
396 | } | |
397 | #endif | |
398 | ||
399 | void ensure_allinstfiles_available(void) { | |
400 | struct pkg_array array; | |
401 | struct pkginfo *pkg; | |
402 | struct progress progress; | |
403 | int i; | |
404 | ||
405 | if (allpackagesdone) return; | |
406 | if (saidread < PKG_FILESDB_LOAD_DONE) { | |
407 | int max = pkg_db_count_pkg(); | |
408 | ||
409 | saidread = PKG_FILESDB_LOAD_INPROGRESS; | |
410 | progress_init(&progress, _("(Reading database ... "), max); | |
411 | } | |
412 | ||
413 | pkg_array_init_from_db(&array); | |
414 | ||
415 | pkg_files_optimize_load(&array); | |
416 | ||
417 | for (i = 0; i < array.n_pkgs; i++) { | |
418 | pkg = array.pkgs[i]; | |
419 | ensure_packagefiles_available(pkg); | |
420 | ||
421 | if (saidread == PKG_FILESDB_LOAD_INPROGRESS) | |
422 | progress_step(&progress); | |
423 | } | |
424 | ||
425 | pkg_array_destroy(&array); | |
426 | ||
427 | allpackagesdone = true; | |
428 | ||
429 | if (saidread == PKG_FILESDB_LOAD_INPROGRESS) { | |
430 | progress_done(&progress); | |
431 | printf(P_("%d file or directory currently installed.)\n", | |
432 | "%d files and directories currently installed.)\n", nfiles), | |
433 | nfiles); | |
434 | saidread = PKG_FILESDB_LOAD_DONE; | |
435 | } | |
436 | } | |
437 | ||
438 | void ensure_allinstfiles_available_quiet(void) { | |
439 | saidread = PKG_FILESDB_LOAD_DONE; | |
440 | ensure_allinstfiles_available(); | |
441 | } | |
442 | ||
443 | /* | |
444 | * If mask is nonzero, will not write any file whose filenamenode | |
445 | * has any flag bits set in mask. | |
446 | */ | |
447 | void | |
448 | write_filelist_except(struct pkginfo *pkg, struct pkgbin *pkgbin, | |
449 | struct fileinlist *list, enum filenamenode_flags mask) | |
450 | { | |
451 | struct atomic_file *file; | |
452 | struct fileinlist *node; | |
453 | const char *listfile; | |
454 | ||
455 | listfile = pkg_infodb_get_file(pkg, pkgbin, LISTFILE); | |
456 | ||
457 | file = atomic_file_new(listfile, 0); | |
458 | atomic_file_open(file); | |
459 | ||
460 | for (node = list; node; node = node->next) { | |
461 | if (!(mask && (node->namenode->flags & mask))) { | |
462 | fputs(node->namenode->name, file->fp); | |
463 | putc('\n', file->fp); | |
464 | } | |
465 | } | |
466 | ||
467 | atomic_file_sync(file); | |
468 | atomic_file_close(file); | |
469 | atomic_file_commit(file); | |
470 | atomic_file_free(file); | |
471 | ||
472 | dir_sync_path(pkg_infodb_get_dir()); | |
473 | ||
474 | note_must_reread_files_inpackage(pkg); | |
475 | } | |
476 | ||
477 | /* | |
478 | * Initializes an iterator that appears to go through the file | |
479 | * list ‘files’ in reverse order, returning the namenode from | |
480 | * each. What actually happens is that we walk the list here, | |
481 | * building up a reverse list, and then peel it apart one | |
482 | * entry at a time. | |
483 | */ | |
484 | void | |
485 | reversefilelist_init(struct reversefilelistiter *iter, struct fileinlist *files) | |
486 | { | |
487 | struct fileinlist *newent; | |
488 | ||
489 | iter->todo = NULL; | |
490 | while (files) { | |
491 | newent= m_malloc(sizeof(struct fileinlist)); | |
492 | newent->namenode= files->namenode; | |
493 | newent->next = iter->todo; | |
494 | iter->todo = newent; | |
495 | files= files->next; | |
496 | } | |
497 | } | |
498 | ||
499 | struct filenamenode * | |
500 | reversefilelist_next(struct reversefilelistiter *iter) | |
501 | { | |
502 | struct filenamenode *ret; | |
503 | struct fileinlist *todo; | |
504 | ||
505 | todo = iter->todo; | |
506 | if (!todo) | |
507 | return NULL; | |
508 | ret= todo->namenode; | |
509 | iter->todo = todo->next; | |
510 | free(todo); | |
511 | return ret; | |
512 | } | |
513 | ||
514 | /* | |
515 | * Clients must call this function to clean up the reversefilelistiter | |
516 | * if they wish to break out of the iteration before it is all done. | |
517 | * Calling this function is not necessary if reversefilelist_next has | |
518 | * been called until it returned 0. | |
519 | */ | |
520 | void | |
521 | reversefilelist_abort(struct reversefilelistiter *iter) | |
522 | { | |
523 | while (reversefilelist_next(iter)); | |
524 | } | |
525 | ||
526 | struct fileiterator { | |
527 | struct filenamenode *namenode; | |
528 | int nbinn; | |
529 | }; | |
530 | ||
531 | /* This must always be a prime for optimal performance. | |
532 | * This is the closest one to 2^18 (262144). */ | |
533 | #define BINS 262139 | |
534 | ||
535 | static struct filenamenode *bins[BINS]; | |
536 | ||
537 | struct fileiterator * | |
538 | files_db_iter_new(void) | |
539 | { | |
540 | struct fileiterator *iter; | |
541 | ||
542 | iter = m_malloc(sizeof(struct fileiterator)); | |
543 | iter->namenode = NULL; | |
544 | iter->nbinn = 0; | |
545 | ||
546 | return iter; | |
547 | } | |
548 | ||
549 | struct filenamenode * | |
550 | files_db_iter_next(struct fileiterator *iter) | |
551 | { | |
552 | struct filenamenode *r= NULL; | |
553 | ||
554 | while (!iter->namenode) { | |
555 | if (iter->nbinn >= BINS) | |
556 | return NULL; | |
557 | iter->namenode = bins[iter->nbinn++]; | |
558 | } | |
559 | r = iter->namenode; | |
560 | iter->namenode = r->next; | |
561 | ||
562 | return r; | |
563 | } | |
564 | ||
565 | void | |
566 | files_db_iter_free(struct fileiterator *iter) | |
567 | { | |
568 | free(iter); | |
569 | } | |
570 | ||
571 | void filesdbinit(void) { | |
572 | struct filenamenode *fnn; | |
573 | int i; | |
574 | ||
575 | for (i=0; i<BINS; i++) | |
576 | for (fnn= bins[i]; fnn; fnn= fnn->next) { | |
577 | fnn->flags= 0; | |
578 | fnn->oldhash = NULL; | |
579 | fnn->newhash = EMPTYHASHFLAG; | |
580 | fnn->filestat = NULL; | |
581 | } | |
582 | } | |
583 | ||
584 | void | |
585 | files_db_reset(void) | |
586 | { | |
587 | int i; | |
588 | ||
589 | for (i = 0; i < BINS; i++) | |
590 | bins[i] = NULL; | |
591 | } | |
592 | ||
593 | struct filenamenode *findnamenode(const char *name, enum fnnflags flags) { | |
594 | struct filenamenode **pointerp, *newnode; | |
595 | const char *orig_name = name; | |
596 | ||
597 | /* We skip initial slashes and ‘./’ pairs, and add our own single | |
598 | * leading slash. */ | |
599 | name = path_skip_slash_dotslash(name); | |
600 | ||
601 | pointerp = bins + (str_fnv_hash(name) % (BINS)); | |
602 | while (*pointerp) { | |
603 | /* XXX: Why is the assert needed? It's checking already added entries. */ | |
604 | assert((*pointerp)->name[0] == '/'); | |
605 | if (strcmp((*pointerp)->name + 1, name) == 0) | |
606 | break; | |
607 | pointerp= &(*pointerp)->next; | |
608 | } | |
609 | if (*pointerp) return *pointerp; | |
610 | ||
611 | if (flags & fnn_nonew) | |
612 | return NULL; | |
613 | ||
614 | newnode= nfmalloc(sizeof(struct filenamenode)); | |
615 | newnode->packages = NULL; | |
616 | if((flags & fnn_nocopy) && name > orig_name && name[-1] == '/') | |
617 | newnode->name = name - 1; | |
618 | else { | |
619 | char *newname= nfmalloc(strlen(name)+2); | |
620 | newname[0]= '/'; strcpy(newname+1,name); | |
621 | newnode->name= newname; | |
622 | } | |
623 | newnode->flags= 0; | |
624 | newnode->next = NULL; | |
625 | newnode->divert = NULL; | |
626 | newnode->statoverride = NULL; | |
627 | newnode->oldhash = NULL; | |
628 | newnode->newhash = EMPTYHASHFLAG; | |
629 | newnode->filestat = NULL; | |
630 | newnode->trig_interested = NULL; | |
631 | *pointerp= newnode; | |
632 | nfiles++; | |
633 | ||
634 | return newnode; | |
635 | } |