Tue Apr 23 22:12:16 2024 UTC (16d)
makefs: Sort directory contents by name (Jan-Benedict Glaw)

`makefs` inserts nodes into its internal data structures in the order as
returned by `readdir()` calls. As this is unpredictable, sort entries by
name before creating the target filesystem.

  This is done by first converting the (per-directory) linked list into
a plain array, sort it, finally re-link the list. Special case for the
sorting function: The "." directory entry seems to be ment to be always
at the front, so always check that first.


(christos)
diff -r1.33 -r1.34 src/usr.sbin/makefs/walk.c

cvs diff -r1.33 -r1.34 src/usr.sbin/makefs/walk.c (expand / switch to unified diff)

--- src/usr.sbin/makefs/walk.c 2023/12/28 12:13:55 1.33
+++ src/usr.sbin/makefs/walk.c 2024/04/23 22:12:16 1.34
@@ -1,14 +1,14 @@ @@ -1,14 +1,14 @@
1/* $NetBSD: walk.c,v 1.33 2023/12/28 12:13:55 tsutsui Exp $ */ 1/* $NetBSD: walk.c,v 1.34 2024/04/23 22:12:16 christos Exp $ */
2 2
3/* 3/*
4 * Copyright (c) 2001 Wasabi Systems, Inc. 4 * Copyright (c) 2001 Wasabi Systems, Inc.
5 * All rights reserved. 5 * All rights reserved.
6 * 6 *
7 * Written by Luke Mewburn for Wasabi Systems, Inc. 7 * Written by Luke Mewburn for Wasabi Systems, Inc.
8 * 8 *
9 * Redistribution and use in source and binary forms, with or without 9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions 10 * modification, are permitted provided that the following conditions
11 * are met: 11 * are met:
12 * 1. Redistributions of source code must retain the above copyright 12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer. 13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright 14 * 2. Redistributions in binary form must reproduce the above copyright
@@ -31,72 +31,93 @@ @@ -31,72 +31,93 @@
31 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 31 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 32 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35 * POSSIBILITY OF SUCH DAMAGE. 35 * POSSIBILITY OF SUCH DAMAGE.
36 */ 36 */
37 37
38#if HAVE_NBTOOL_CONFIG_H 38#if HAVE_NBTOOL_CONFIG_H
39#include "nbtool_config.h" 39#include "nbtool_config.h"
40#endif 40#endif
41 41
42#include <sys/cdefs.h> 42#include <sys/cdefs.h>
43#if defined(__RCSID) && !defined(__lint) 43#if defined(__RCSID) && !defined(__lint)
44__RCSID("$NetBSD: walk.c,v 1.33 2023/12/28 12:13:55 tsutsui Exp $"); 44__RCSID("$NetBSD: walk.c,v 1.34 2024/04/23 22:12:16 christos Exp $");
45#endif /* !__lint */ 45#endif /* !__lint */
46 46
47#include <sys/param.h> 47#include <sys/param.h>
48#include <sys/stat.h> 48#include <sys/stat.h>
49 49
50#include <assert.h> 50#include <assert.h>
51#include <errno.h> 51#include <errno.h>
52#include <fcntl.h> 52#include <fcntl.h>
53#include <stdio.h> 53#include <stdio.h>
54#include <dirent.h> 54#include <dirent.h>
55#include <stdlib.h> 55#include <stdlib.h>
56#include <string.h> 56#include <string.h>
57#include <unistd.h> 57#include <unistd.h>
58#include <util.h> 58#include <util.h>
59 59
60#include "makefs.h" 60#include "makefs.h"
61#include "mtree.h" 61#include "mtree.h"
62 62
63static void apply_specdir(const char *, NODE *, fsnode *, int); 63static void apply_specdir(const char *, NODE *, fsnode *, int);
64static void apply_specentry(const char *, NODE *, fsnode *); 64static void apply_specentry(const char *, NODE *, fsnode *);
65static fsnode *create_fsnode(const char *, const char *, const char *, 65static fsnode *create_fsnode(const char *, const char *, const char *,
66 struct stat *); 66 struct stat *);
67static fsinode *link_check(fsinode *); 67static fsinode *link_check(fsinode *);
68 68
 69/*
 70 * fsnode_cmp --
 71 * This function is used by `qsort` so sort one directory's
 72 * entries. `.` is always first, sollowed by anything else
 73 * as compared by `strcmp()`.
 74 */
 75static int
 76fsnode_cmp (const void *_left, const void *_right)
 77{
 78 const fsnode * const left = *(const fsnode * const *)_left;
 79 const fsnode * const right = *(const fsnode * const *)_right;
 80
 81 if (strcmp (left->name, ".") == 0)
 82 return -1;
 83 if (strcmp (right->name, ".") == 0)
 84 return 1;
 85 return strcmp (left->name, right->name);
 86}
69 87
70/* 88/*
71 * walk_dir -- 89 * walk_dir --
72 * build a tree of fsnodes from `root' and `dir', with a parent 90 * build a tree of fsnodes from `root' and `dir', with a parent
73 * fsnode of `parent' (which may be NULL for the root of the tree). 91 * fsnode of `parent' (which may be NULL for the root of the tree).
74 * append the tree to a fsnode of `join' if it is not NULL. 92 * append the tree to a fsnode of `join' if it is not NULL.
75 * each "level" is a directory, with the "." entry guaranteed to be 93 * each "level" is a directory, with the "." entry guaranteed to be
76 * at the start of the list, and without ".." entries. 94 * at the start of the list, and without ".." entries.
77 */ 95 */
78fsnode * 96fsnode *
79walk_dir(const char *root, const char *dir, fsnode *parent, fsnode *join, 97walk_dir(const char *root, const char *dir, fsnode *parent, fsnode *join,
80 int replace, int follow) 98 int replace, int follow)
81{ 99{
82 fsnode *first, *cur, *prev, *last; 100 fsnode *first, *cur, *prev, *last;
83 DIR *dirp; 101 DIR *dirp;
84 struct dirent *dent; 102 struct dirent *dent;
85 char path[MAXPATHLEN + 1]; 103 char path[MAXPATHLEN + 1];
86 struct stat stbuf; 104 struct stat stbuf;
87 char *name, *rp; 105 char *name, *rp;
88 int dot, len; 106 int dot, len;
89 107
 108 fsnode **list, **listptr;
 109 int num = 0;
 110
90 assert(root != NULL); 111 assert(root != NULL);
91 assert(dir != NULL); 112 assert(dir != NULL);
92 113
93 len = snprintf(path, sizeof(path), "%s/%s", root, dir); 114 len = snprintf(path, sizeof(path), "%s/%s", root, dir);
94 if (len >= (int)sizeof(path)) 115 if (len >= (int)sizeof(path))
95 errx(EXIT_FAILURE, "Pathname too long."); 116 errx(EXIT_FAILURE, "Pathname too long.");
96 if (debug & DEBUG_WALK_DIR) 117 if (debug & DEBUG_WALK_DIR)
97 printf("walk_dir: %s %p\n", path, parent); 118 printf("walk_dir: %s %p\n", path, parent);
98 if ((dirp = opendir(path)) == NULL) 119 if ((dirp = opendir(path)) == NULL)
99 err(EXIT_FAILURE, "Can't opendir `%s'", path); 120 err(EXIT_FAILURE, "Can't opendir `%s'", path);
100 rp = path + strlen(root) + 1; 121 rp = path + strlen(root) + 1;
101 if (join != NULL) { 122 if (join != NULL) {
102 first = cur = join; 123 first = cur = join;
@@ -231,27 +252,56 @@ walk_dir(const char *root, const char *d @@ -231,27 +252,56 @@ walk_dir(const char *root, const char *d
231 llen = readlink(path, slink, sizeof(slink) - 1); 252 llen = readlink(path, slink, sizeof(slink) - 1);
232 if (llen == -1) 253 if (llen == -1)
233 err(EXIT_FAILURE, "Readlink `%s'", path); 254 err(EXIT_FAILURE, "Readlink `%s'", path);
234 slink[llen] = '\0'; 255 slink[llen] = '\0';
235 cur->symlink = estrdup(slink); 256 cur->symlink = estrdup(slink);
236 } 257 }
237 } 258 }
238 assert(first != NULL); 259 assert(first != NULL);
239 if (join == NULL) 260 if (join == NULL)
240 for (cur = first->next; cur != NULL; cur = cur->next) 261 for (cur = first->next; cur != NULL; cur = cur->next)
241 cur->first = first; 262 cur->first = first;
242 if (closedir(dirp) == -1) 263 if (closedir(dirp) == -1)
243 err(EXIT_FAILURE, "Can't closedir `%s/%s'", root, dir); 264 err(EXIT_FAILURE, "Can't closedir `%s/%s'", root, dir);
244 return (first); 265
 266 /*
 267 * Sort entries.
 268 */
 269 /* Create a plain list: Count, alloc, add. */
 270 for (fsnode *tmp = first; tmp; tmp = tmp->next) {
 271 num++;
 272 if (debug & DEBUG_DUMP_FSNODES_VERBOSE)
 273 printf ("pre sort: %s %s %s\n", root, dir, tmp->name);
 274 }
 275 list = listptr = ecalloc (num, sizeof (*list));
 276 for (fsnode *tmp = first; tmp; tmp = tmp->next)
 277 *listptr++ = tmp;
 278 /* Sort plain list. */
 279 qsort (list, num, sizeof (*list), &fsnode_cmp);
 280 /* Rewire. */
 281 for (int i = 0; i < num - 1; ++i)
 282 list[i]->next = list[i+1];
 283 list[num - 1]->next = NULL;
 284 first = list[0];
 285 /* Check `first` to be ".". */
 286 assert (strcmp (first->name, ".") == 0);
 287 /* Free. */
 288 free (list);
 289 /* Dump sorted state. */
 290 if (debug & DEBUG_DUMP_FSNODES_VERBOSE)
 291 for (fsnode *tmp = first; tmp; tmp = tmp->next)
 292 printf ("post sort: %s %s %s\n", root, dir, tmp->name);
 293
 294 return first;
245} 295}
246 296
247static fsnode * 297static fsnode *
248create_fsnode(const char *root, const char *path, const char *name, 298create_fsnode(const char *root, const char *path, const char *name,
249 struct stat *stbuf) 299 struct stat *stbuf)
250{ 300{
251 fsnode *cur; 301 fsnode *cur;
252 302
253 cur = ecalloc(1, sizeof(*cur)); 303 cur = ecalloc(1, sizeof(*cur));
254 cur->path = estrdup(path); 304 cur->path = estrdup(path);
255 cur->name = estrdup(name); 305 cur->name = estrdup(name);
256 cur->inode = ecalloc(1, sizeof(*cur->inode)); 306 cur->inode = ecalloc(1, sizeof(*cur->inode));
257 cur->root = root; 307 cur->root = root;