1 /* index.c: module index file shared functions for modprobe and depmod
2 Copyright (C) 2008 Alan Jenkins <alan-jenkins@tuffmail.co.uk>.
4 These programs are free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with these programs. If not, see <http://www.gnu.org/licenses/>.
18 #include <arpa/inet.h> /* htonl */
32 * Index abstract data type (used only by depmod)
35 struct index_node
*index_create()
37 struct index_node
*node
;
39 node
= NOFAIL(calloc(sizeof(struct index_node
), 1));
40 node
->prefix
= NOFAIL(strdup(""));
41 node
->first
= INDEX_CHILDMAX
;
46 void index_values_free(struct index_value
*values
)
49 struct index_value
*value
= values
;
56 void index_destroy(struct index_node
*node
)
60 for (c
= node
->first
; c
<= node
->last
; c
++) {
61 struct index_node
*child
= node
->children
[c
];
66 index_values_free(node
->values
);
71 static void index__checkstring(const char *str
)
75 for (i
= 0; str
[i
]; i
++) {
78 if (ch
>= INDEX_CHILDMAX
)
79 fatal("Module index: bad character '%c'=0x%x - only 7-bit ASCII is supported:"
80 "\n%s\n", (char) ch
, (int) ch
, str
);
84 static int add_value(struct index_value
**values
,
85 const char *value
, unsigned int priority
)
87 struct index_value
*v
;
91 /* report the presence of duplicate values */
92 for (v
= *values
; v
; v
= v
->next
) {
93 if (streq(v
->value
, value
))
97 /* find position to insert value */
98 while (*values
&& (*values
)->priority
< priority
)
99 values
= &(*values
)->next
;
102 v
= NOFAIL(calloc(sizeof(struct index_value
) + len
+ 1, 1));
104 v
->priority
= priority
;
105 memcpy(v
->value
, value
, len
+ 1);
111 int index_insert(struct index_node
*node
, const char *key
,
112 const char *value
, unsigned int priority
)
114 int i
= 0; /* index within str */
117 index__checkstring(key
);
118 index__checkstring(value
);
121 int j
; /* index within node->prefix */
123 /* Ensure node->prefix is a prefix of &str[i].
124 If it is not already, then we must split node. */
125 for (j
= 0; node
->prefix
[j
]; j
++) {
126 ch
= node
->prefix
[j
];
128 if (ch
!= key
[i
+j
]) {
129 char *prefix
= node
->prefix
;
130 struct index_node
*n
;
132 /* New child is copy of node with prefix[j+1..N] */
133 n
= NOFAIL(calloc(sizeof(struct index_node
), 1));
134 memcpy(n
, node
, sizeof(struct index_node
));
135 n
->prefix
= NOFAIL(strdup(&prefix
[j
+1]));
137 /* Parent has prefix[0..j], child at prefix[j] */
138 memset(node
, 0, sizeof(struct index_node
));
140 node
->prefix
= prefix
;
143 node
->children
[ch
] = n
;
148 /* j is now length of node->prefix */
153 return add_value(&node
->values
, value
, priority
);
155 if (!node
->children
[ch
]) {
156 struct index_node
*child
;
158 if (ch
< node
->first
)
162 node
->children
[ch
] = NOFAIL(calloc(sizeof(struct index_node
), 1));
164 child
= node
->children
[ch
];
165 child
->prefix
= NOFAIL(strdup(&key
[i
+1]));
166 child
->first
= INDEX_CHILDMAX
;
167 add_value(&child
->values
, value
, priority
);
172 /* Descend into child node and continue */
173 node
= node
->children
[ch
];
178 static int index__haschildren(const struct index_node
*node
)
180 return node
->first
< INDEX_CHILDMAX
;
183 /* Recursive post-order traversal
185 Pre-order would make for better read-side buffering / readahead / caching.
186 (post-order means you go backwards in the file as you descend the tree).
187 However, index reading is already fast enough.
188 Pre-order is simpler for writing, and depmod is already slow.
190 static uint32_t index_write__node(const struct index_node
*node
, FILE *out
)
192 uint32_t *child_offs
= NULL
;
199 /* Write children and save their offsets */
200 if (index__haschildren(node
)) {
201 const struct index_node
*child
;
204 child_count
= node
->last
- node
->first
+ 1;
205 child_offs
= NOFAIL(malloc(child_count
* sizeof(uint32_t)));
207 for (i
= 0; i
< child_count
; i
++) {
208 child
= node
->children
[node
->first
+ i
];
209 child_offs
[i
] = htonl(index_write__node(child
, out
));
213 /* Now write this node */
216 if (node
->prefix
[0]) {
217 fputs(node
->prefix
, out
);
219 offset
|= INDEX_NODE_PREFIX
;
223 fputc(node
->first
, out
);
224 fputc(node
->last
, out
);
225 fwrite(child_offs
, sizeof(uint32_t), child_count
, out
);
227 offset
|= INDEX_NODE_CHILDS
;
231 const struct index_value
*v
;
232 unsigned int value_count
;
236 for (v
= node
->values
; v
!= NULL
; v
= v
->next
)
238 u
= htonl(value_count
);
239 fwrite(&u
, sizeof(u
), 1, out
);
241 for (v
= node
->values
; v
!= NULL
; v
= v
->next
) {
242 u
= htonl(v
->priority
);
243 fwrite(&u
, sizeof(u
), 1, out
);
244 fputs(v
->value
, out
);
247 offset
|= INDEX_NODE_VALUES
;
253 void index_write(const struct index_node
*node
, FILE *out
)
255 long initial_offset
, final_offset
;
258 u
= htonl(INDEX_MAGIC
);
259 fwrite(&u
, sizeof(u
), 1, out
);
260 u
= htonl(INDEX_VERSION
);
261 fwrite(&u
, sizeof(u
), 1, out
);
263 /* Second word is reserved for the offset of the root node */
264 initial_offset
= ftell(out
);
266 fwrite(&u
, sizeof(uint32_t), 1, out
);
269 u
= htonl(index_write__node(node
, out
));
271 /* Update first word */
272 final_offset
= ftell(out
);
273 fseek(out
, initial_offset
, SEEK_SET
);
274 fwrite(&u
, sizeof(uint32_t), 1, out
);
275 fseek(out
, final_offset
, SEEK_SET
);
280 static void read_error()
282 fatal("Module index: unexpected error: %s\n"
283 "Try re-running depmod\n", errno
? strerror(errno
) : "EOF");
286 static int read_char(FILE *in
)
291 ch
= getc_unlocked(in
);
297 static uint32_t read_long(FILE *in
)
302 if (fread(&l
, sizeof(uint32_t), 1, in
) <= 0)
308 * Buffer abstract data type
310 * Used internally to store the current path during tree traversal.
311 * They help build wildcard key strings to pass to fnmatch(),
312 * as well as building values of matching keys.
321 static void buf__realloc(struct buffer
*buf
, unsigned size
)
323 if (size
> buf
->size
) {
324 buf
->bytes
= NOFAIL(realloc(buf
->bytes
, size
));
329 static struct buffer
*buf_create()
333 buf
= NOFAIL(calloc(sizeof(struct buffer
), 1));
334 buf__realloc(buf
, 256);
338 static void buf_destroy(struct buffer
*buf
)
344 /* Destroy buffer and return a copy as a C string */
345 static char *buf_detach(struct buffer
*buf
)
349 bytes
= NOFAIL(realloc(buf
->bytes
, buf
->used
+ 1));
350 bytes
[buf
->used
] = '\0';
356 /* Return a C string owned by the buffer
357 (invalidated if the buffer is changed).
359 static const char *buf_str(struct buffer
*buf
)
361 buf__realloc(buf
, buf
->used
+ 1);
362 buf
->bytes
[buf
->used
] = '\0';
366 static int buf_fwrite(struct buffer
*buf
, FILE *out
)
368 return fwrite(buf
->bytes
, 1, buf
->used
, out
);
371 static void buf_pushchar(struct buffer
*buf
, char ch
)
373 buf__realloc(buf
, buf
->used
+ 1);
374 buf
->bytes
[buf
->used
] = ch
;
378 static unsigned buf_pushchars(struct buffer
*buf
, const char *str
)
383 while ((ch
= str
[i
])) {
384 buf_pushchar(buf
, ch
);
390 /* like buf_pushchars(), but the string comes from a file */
391 static unsigned buf_freadchars(struct buffer
*buf
, FILE *in
)
396 while ((ch
= read_char(in
))) {
397 buf_pushchar(buf
, ch
);
404 static void buf_popchar(struct buffer
*buf
)
409 static void buf_popchars(struct buffer
*buf
, unsigned n
)
414 static void buf_clear(struct buffer
*buf
)
420 * Index file searching (used only by modprobe)
423 struct index_node_f
{
425 char *prefix
; /* path compression */
426 struct index_value
*values
;
427 unsigned char first
; /* range of child nodes */
429 uint32_t children
[0];
432 static struct index_node_f
*index_read(FILE *in
, uint32_t offset
)
434 struct index_node_f
*node
;
436 int i
, child_count
= 0;
438 if ((offset
& INDEX_NODE_MASK
) == 0)
441 fseek(in
, offset
& INDEX_NODE_MASK
, SEEK_SET
);
443 if (offset
& INDEX_NODE_PREFIX
) {
444 struct buffer
*buf
= buf_create();
445 buf_freadchars(buf
, in
);
446 prefix
= buf_detach(buf
);
448 prefix
= NOFAIL(strdup(""));
450 if (offset
& INDEX_NODE_CHILDS
) {
451 char first
= read_char(in
);
452 char last
= read_char(in
);
453 child_count
= last
- first
+ 1;
455 node
= NOFAIL(malloc(sizeof(struct index_node_f
) +
456 sizeof(uint32_t) * child_count
));
461 for (i
= 0; i
< child_count
; i
++)
462 node
->children
[i
] = read_long(in
);
464 node
= NOFAIL(malloc(sizeof(struct index_node_f
)));
465 node
->first
= INDEX_CHILDMAX
;
470 if (offset
& INDEX_NODE_VALUES
) {
472 struct buffer
*buf
= buf_create();
474 unsigned int priority
;
476 value_count
= read_long(in
);
478 while (value_count
--) {
479 priority
= read_long(in
);
480 buf_freadchars(buf
, in
);
481 value
= buf_str(buf
);
482 add_value(&node
->values
, value
, priority
);
488 node
->prefix
= prefix
;
493 static void index_close(struct index_node_f
*node
)
496 index_values_free(node
->values
);
502 uint32_t root_offset
;
505 /* Failures are silent; modprobe will fall back to text files */
506 struct index_file
*index_file_open(const char *filename
)
509 uint32_t magic
, version
;
510 struct index_file
*new;
512 file
= fopen(filename
, "r");
517 magic
= read_long(file
);
518 if (magic
!= INDEX_MAGIC
)
521 version
= read_long(file
);
522 if (version
>> 16 != INDEX_VERSION_MAJOR
)
525 new = NOFAIL(malloc(sizeof(struct index_file
)));
527 new->root_offset
= read_long(new->file
);
533 void index_file_close(struct index_file
*index
)
540 static struct index_node_f
*index_readroot(struct index_file
*in
)
542 return index_read(in
->file
, in
->root_offset
);
545 static struct index_node_f
*index_readchild(const struct index_node_f
*parent
,
548 if (parent
->first
<= ch
&& ch
<= parent
->last
)
549 return index_read(parent
->file
,
550 parent
->children
[ch
- parent
->first
]);
556 * Dump all strings as lines in a plain text file.
559 static void index_dump_node(struct index_node_f
*node
,
564 struct index_value
*v
;
567 pushed
= buf_pushchars(buf
, node
->prefix
);
569 for (v
= node
->values
; v
!= NULL
; v
= v
->next
) {
571 buf_fwrite(buf
, out
);
573 fputs(v
->value
, out
);
577 for (ch
= node
->first
; ch
<= node
->last
; ch
++) {
578 struct index_node_f
*child
= index_readchild(node
, ch
);
583 buf_pushchar(buf
, ch
);
584 index_dump_node(child
, buf
, out
, prefix
);
588 buf_popchars(buf
, pushed
);
592 void index_dump(struct index_file
*in
, FILE *out
, const char *prefix
)
594 struct index_node_f
*root
;
598 root
= index_readroot(in
);
599 index_dump_node(root
, buf
, out
, prefix
);
604 * Search the index for a key
606 * Returns the value of the first match
608 * The recursive functions free their node argument (using index_close).
611 char *index_search(struct index_file
*in
, const char *key
);
612 static char *index_search__node(struct index_node_f
*node
, const char *key
, int i
);
614 char *index_search(struct index_file
*in
, const char *key
)
616 struct index_node_f
*root
;
619 root
= index_readroot(in
);
620 value
= index_search__node(root
, key
, 0);
625 static char *index_search__node(struct index_node_f
*node
, const char *key
, int i
)
627 struct index_node_f
*child
;
632 for (j
= 0; node
->prefix
[j
]; j
++) {
633 ch
= node
->prefix
[j
];
635 if (ch
!= key
[i
+j
]) {
642 if (key
[i
] == '\0') {
644 return strdup(node
->values
[0].value
);
649 child
= index_readchild(node
, key
[i
]);
659 * Search the index for a key. The index may contain wildcards.
661 * Returns a list of all the values of matching keys.
664 /* Level 1: interface function */
665 struct index_value
*index_searchwild(struct index_file
*in
, const char *key
);
667 /* Level 2: descend the tree (until we hit a wildcard) */
668 static void index_searchwild__node(struct index_node_f
*node
,
670 const char *key
, int i
,
671 struct index_value
**out
);
673 /* Level 3: traverse a sub-keyspace which starts with a wildcard,
676 static void index_searchwild__all(struct index_node_f
*node
, int j
,
679 struct index_value
**out
);
681 /* Level 4: add all the values from a matching node */
682 static void index_searchwild__allvalues(struct index_node_f
*node
,
683 struct index_value
**out
);
686 struct index_value
*index_searchwild(struct index_file
*in
, const char *key
)
688 struct index_node_f
*root
= index_readroot(in
);
689 struct buffer
*buf
= buf_create();
690 struct index_value
*out
= NULL
;
692 index_searchwild__node(root
, buf
, key
, 0, &out
);
697 static void index_searchwild__node(struct index_node_f
*node
,
699 const char *key
, int i
,
700 struct index_value
**out
)
702 struct index_node_f
*child
;
707 for (j
= 0; node
->prefix
[j
]; j
++) {
708 ch
= node
->prefix
[j
];
710 if (ch
== '*' || ch
== '?' || ch
== '[') {
711 index_searchwild__all(node
, j
, buf
,
716 if (ch
!= key
[i
+j
]) {
723 child
= index_readchild(node
, '*');
725 buf_pushchar(buf
, '*');
726 index_searchwild__all(child
, 0, buf
, &key
[i
], out
);
730 child
= index_readchild(node
, '?');
732 buf_pushchar(buf
, '?');
733 index_searchwild__all(child
, 0, buf
, &key
[i
], out
);
737 child
= index_readchild(node
, '[');
739 buf_pushchar(buf
, '[');
740 index_searchwild__all(child
, 0, buf
, &key
[i
], out
);
744 if (key
[i
] == '\0') {
745 index_searchwild__allvalues(node
, out
);
750 child
= index_readchild(node
, key
[i
]);
757 static void index_searchwild__all(struct index_node_f
*node
, int j
,
760 struct index_value
**out
)
765 while (node
->prefix
[j
]) {
766 ch
= node
->prefix
[j
];
768 buf_pushchar(buf
, ch
);
773 for (ch
= node
->first
; ch
<= node
->last
; ch
++) {
774 struct index_node_f
*child
= index_readchild(node
, ch
);
779 buf_pushchar(buf
, ch
);
780 index_searchwild__all(child
, 0, buf
, subkey
, out
);
785 if (fnmatch(buf_str(buf
), subkey
, 0) == 0)
786 index_searchwild__allvalues(node
, out
);
790 buf_popchars(buf
, pushed
);
793 static void index_searchwild__allvalues(struct index_node_f
*node
,
794 struct index_value
**out
)
796 struct index_value
*v
;
798 for (v
= node
->values
; v
!= NULL
; v
= v
->next
)
799 add_value(out
, v
->value
, v
->priority
);