steps to support modern FreeBSD. After Robert Watson <rwatson@FreeBSD.org> and Alec...
[arla.git] / lib / bufdir / fdir.c
bloba8c6c8dcc105e1337d2677ff27e71e61c6d8d174
1 /*
2 * Copyright (c) 1995 - 2006 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of the Institute nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
35 * Routines for reading an AFS directory
38 #ifndef _KERNEL
39 #include <config.h>
41 RCSID("$Id$") ;
43 #include <sys/types.h>
44 #include <sys/errno.h>
45 #include <stdio.h>
46 #include <unistd.h>
47 #include <assert.h>
48 #include <rx/rx.h>
49 #else /* _KERNEL */
50 #include <nnpfs_locl.h>
51 #endif /* _KERNEL */
53 #include <afs_dir.h>
54 #include <fdir.h>
57 * Hash the filename of one entry.
60 static unsigned
61 hashentry (const char *sentry)
63 int s = 0, h;
64 const unsigned char *entry = (const unsigned char *)sentry;
66 while (*entry)
67 s = s * 173 + *entry++;
68 h = s & (ADIRHASHSIZE - 1);
69 if (h == 0)
70 return h;
71 else if( s < 0 )
72 h = ADIRHASHSIZE - h;
73 return h;
77 * Return the number of additional DirEntries used by an entry with
78 * the filename `name`.
81 static unsigned
82 additional_entries (const char *filename,
83 const char *raw_utf,
84 const char *norm_utf)
86 static DirEntry dummy;
88 size_t len = (strlen(filename) - sizeof(dummy.name) + 1
89 + sizeof(DirEntry) - 1);
90 if (raw_utf) {
91 /* align to 4 byte boundery */
92 if (len % 4)
93 len += 4 - (len % 4);
94 len += sizeof(DirEntry2);
95 len += strlen(raw_utf);
96 if (norm_utf)
97 len += strlen(norm_utf);
99 return len / sizeof(DirEntry);
103 * Return a pointer to page number `pageno'.
106 static DirPage1 *
107 getpage (DirPage0 *page0, unsigned pageno)
109 return (DirPage1 *)((char *)page0 + AFSDIR_PAGESIZE * pageno);
113 * Return the entry `num' in the directory `page0'.
114 * The directory must be continuous in memory after page0.
117 static DirEntry *
118 getentry (DirPage0 *page0,
119 unsigned short num,
120 size_t npages)
122 DirPage1 *page;
123 unsigned idx = num / ENTRIESPERPAGE;
125 if (idx >= npages)
126 return NULL;
128 page = getpage (page0, idx);
130 if (page->header.pg_tag != htons(AFSDIRMAGIC) &&
131 page->header.pg_tag != htons(AFSDIRMAGIC_UTF8))
132 return NULL;
134 return &page->entry[num % ENTRIESPERPAGE];
138 * Return a pointer to the entry with name `name' in the directory `page0'.
141 static DirEntry *
142 find_entry(DirPage0 *page0, const char *name, size_t npages)
144 DirEntry *entry;
145 unsigned short i;
147 for (i = ntohs(page0->dheader.hash[hashentry (name)]);
148 i != 0;
149 i = ntohs(entry->next))
151 entry = getentry (page0, (unsigned short)(i - 1), npages);
152 if (entry == NULL)
153 return NULL;
155 if (strcmp (entry->name, name) == 0)
156 return entry;
158 return NULL;
162 * Return the fid for the entry with `name' in `page0'. `fid' is set
163 * to the fid of that file. `dir' should be the fid of the directory.
164 * Return zero if succesful, and -1 otherwise.
167 static int
168 find_by_name (DirPage0 *page0,
169 const char *name,
170 VenusFid *fid,
171 const VenusFid *dir,
172 unsigned npages)
174 const DirEntry *entry = find_entry (page0, name, npages);
176 if (entry == NULL)
177 return -1;
178 fid->Cell = dir->Cell;
179 fid->fid.Volume = dir->fid.Volume;
180 fid->fid.Vnode = ntohl (entry->fid.Vnode);
181 fid->fid.Unique = ntohl (entry->fid.Unique);
182 return 0;
186 * Return true if slot `off' on `page' is being used.
189 static int
190 used_slot (DirPage1 *page, int off)
192 return page->header.pg_bitmap[off / 8] & (1 << (off % 8));
196 * Return true if slot w/ index `off' on `page' is a valid entry.
199 static int
200 first_slotp (DirPage1 *page, int off)
202 DirEntry *entry = &page->entry[off];
203 if (used_slot(page, off + 1)
204 && (entry->flag & AFSDIR_FIRST))
205 return TRUE;
207 return FALSE;
211 * Is this page `pageno' empty?
214 static int
215 is_page_empty (DirPage0 *page0, unsigned pageno)
217 DirPage1 *page;
218 int i;
220 if (pageno < MAXPAGES)
221 return page0->dheader.map[pageno] == ENTRIESPERPAGE - 1;
222 page = getpage (page0, pageno);
223 if (page->header.pg_bitmap[0] != 1)
224 return 0;
225 for (i = 1; i < sizeof(page->header.pg_bitmap); ++i)
226 if (page->header.pg_bitmap[i] != 0)
227 return 0;
228 return 1;
232 * Lookup `name' in the AFS directory identified by `dir' and return
233 * the Fid in `file'. Return value is 0 or error code.
237 fdir_lookup (fbuf *the_fbuf, const VenusFid *dir,
238 const char *name, VenusFid *file)
240 DirPage0 *page0;
241 unsigned ind;
242 unsigned npages;
243 size_t len = fbuf_len(the_fbuf);
245 page0 = (DirPage0 *)fbuf_buf(the_fbuf);
246 assert (page0);
248 npages = ntohs(page0->header.pg_pgcount);
249 if (npages < len / AFSDIR_PAGESIZE)
250 npages = len / AFSDIR_PAGESIZE;
252 ind = find_by_name (page0, name, file, dir, npages);
254 if (ind == 0)
255 return 0;
256 else
257 return ENOENT;
261 * Return TRUE if dir is empty.
265 fdir_emptyp (fbuf *dir)
267 DirPage0 *page0;
268 unsigned npages;
269 size_t len = fbuf_len(dir);
271 page0 = (DirPage0 *)fbuf_buf(dir);
272 assert (page0);
274 npages = ntohs(page0->header.pg_pgcount);
275 if (npages < len / AFSDIR_PAGESIZE)
276 npages = len / AFSDIR_PAGESIZE;
278 return (npages == 1) && (page0->dheader.map[0] == 49);
282 * Read all entries in the AFS directory identified by `dir' and call
283 * `func' on each entry with the fid, the name, and `arg'.
287 fdir_readdir (fbuf *the_fbuf,
288 fdir_readdir_func func,
289 void *arg,
290 VenusFid dir,
291 uint32_t *offset)
293 DirPage0 *page0;
294 unsigned i, j;
295 VenusFid fid;
296 size_t len = fbuf_len(the_fbuf);
297 unsigned npages;
298 int ret;
300 page0 = (DirPage0 *)fbuf_buf(the_fbuf);
302 assert (page0);
304 npages = ntohs(page0->header.pg_pgcount);
305 if (npages < len / AFSDIR_PAGESIZE)
306 npages = len / AFSDIR_PAGESIZE;
308 if (offset && *offset) {
309 i = *offset / ENTRIESPERPAGE;
310 j = *offset % ENTRIESPERPAGE;
312 if (i == 0 && j < 12)
313 return EINVAL;
314 } else {
315 i = 0;
316 j = 12; /* first slot */
319 for (; i < npages; ++i) {
320 DirPage1 *page = getpage (page0, i);
322 for (; j < ENTRIESPERPAGE - 1; ++j) {
323 if (first_slotp (page, j)) {
324 DirEntry *entry = &page->entry[j];
325 int nentries;
326 char *name;
327 char *raw_utf = NULL;
328 char *norm_utf = NULL;
330 if ((entry->flag & AFSDIR_FIRST) == 0)
331 continue;
333 fid.Cell = dir.Cell;
334 fid.fid.Volume = dir.fid.Volume;
335 fid.fid.Vnode = ntohl (entry->fid.Vnode);
336 fid.fid.Unique = ntohl (entry->fid.Unique);
338 if (entry->flag & AFSDIR_UTFENT) {
339 DirEntry2 *direntry2 = (DirEntry2 *)((char *)entry) + (entry->length << 2);
342 * Check that the end of the entry isn't past end
343 * of page
345 if ((unsigned char *)direntry2 >
346 ((unsigned char *)page) + AFSDIR_PAGESIZE - sizeof(*direntry2))
347 continue;
349 raw_utf = ((char *)entry) + direntry2->raw_offset;
350 if (direntry2->raw_offset != direntry2->norm_offset)
351 norm_utf = ((char *)entry) + direntry2->norm_offset;
354 * Check that the start of the name isn't over
355 * the page, we should really check if its inside
356 * page, but lets skip that for now.
358 if (raw_utf > ((char *)page) + AFSDIR_PAGESIZE)
359 continue;
361 name = raw_utf;
362 } else
363 name = entry->name;
365 nentries = additional_entries(entry->name, raw_utf, norm_utf);
367 /* Break on non-zero return, increase counter if >= 0 */
368 ret = (*func)(&fid, name, arg);
369 if (ret < 0)
370 goto done;
372 j += nentries;
374 if (ret)
375 goto done;
378 j = 0;
381 done:
382 if (offset)
383 *offset = i * ENTRIESPERPAGE + j;
385 return 0;
388 #ifndef _KERNEL
391 * Return non-zero is `the_fbuf' passes casual inspection as a
392 * directory.
396 fdir_dirp (fbuf *the_fbuf)
398 DirPage0 *page0;
399 DirPage1 *page;
400 unsigned num, npages;
401 size_t len = fbuf_len(the_fbuf);
403 if (len < AFSDIR_PAGESIZE)
404 return 0;
406 page0 = (DirPage0 *)fbuf_buf(the_fbuf);
407 assert (page0);
409 npages = ntohs(page0->header.pg_pgcount);
410 if (npages < len / AFSDIR_PAGESIZE)
411 npages = len / AFSDIR_PAGESIZE;
413 for (num = 0; num < npages; num++) {
414 page = getpage (page0, num);
416 if (page->header.pg_tag != htons(AFSDIRMAGIC) &&
417 page->header.pg_tag != htons(AFSDIRMAGIC_UTF8))
418 return 0;
420 return 1;
425 * Change the fid for `name' to `fid'. Return 0 or -1.
428 static int
429 update_fid_by_name (DirPage0 *page0,
430 const char *name,
431 const VenusFid *fid,
432 unsigned npages)
434 DirEntry *entry = find_entry (page0, name, npages);
436 if (entry == NULL)
437 return -1;
439 entry->fid.Vnode = htonl (fid->fid.Vnode);
440 entry->fid.Unique = htonl (fid->fid.Unique);
441 return 0;
445 * Mark slot `off' on `page' as being used.
448 static void
449 set_used (DirPage1 *page, int off)
451 page->header.pg_bitmap[off / 8] |= 1 << (off % 8);
455 * Mark slot `off' on `page' as not being used.
458 static void
459 set_unused (DirPage1 *page, int off)
461 page->header.pg_bitmap[off / 8] &= ~(1 << (off % 8));
465 * Add a new page to the directory in `the_fbuf', returning a pointer
466 * to the new page in `ret_page'.
467 * Return 0 iff succesful.
470 static int
471 create_new_page (DirPage1 **ret_page,
472 fbuf *the_fbuf, int utf8_page)
474 int ret;
475 DirPage1 *page1;
476 size_t len = fbuf_len(the_fbuf);
478 ret = fbuf_truncate (the_fbuf, len + AFSDIR_PAGESIZE);
479 if (ret)
480 return ret;
482 page1 = (DirPage1 *)((char *)fbuf_buf(the_fbuf) + len);
483 page1->header.pg_pgcount = htons(0);
484 if (utf8_page)
485 page1->header.pg_tag = htons(AFSDIRMAGIC_UTF8);
486 else
487 page1->header.pg_tag = htons(AFSDIRMAGIC);
488 page1->header.pg_freecount = ENTRIESPERPAGE - 1;
489 memset (page1->header.pg_bitmap, 0, sizeof(page1->header.pg_bitmap));
490 set_used (page1, 0);
491 *ret_page = page1;
493 return 0;
497 * Create a new entry with name `filename', fid `fid', and next
498 * pointer `next' in the page `page' with page number `pageno'.
499 * Return the index in the page or -1 if unsuccesful.
502 static int
503 add_to_page (DirPage0 *page0,
504 DirPage1 *page,
505 unsigned pageno,
506 const char *filename,
507 const char *raw_name,
508 AFSFid fid,
509 unsigned next)
511 int i, j;
512 unsigned n;
514 n = 1 + additional_entries (filename, raw_name, NULL);
516 if (pageno < MAXPAGES && page0->dheader.map[pageno] < n)
517 return -1;
519 for (i = 0; i < ENTRIESPERPAGE - n;) {
520 for (j = 0; j < n && !used_slot (page, i + j + 1); ++j)
522 if (j == n) {
523 int k;
525 for (k = i + 1; k < i + j + 1; ++k)
526 page->header.pg_bitmap[k / 8] |= (1 << (k % 8));
528 page->entry[i].flag = AFSDIR_FIRST;
529 page->entry[i].length = 0;
530 page->entry[i].next = next;
531 page->entry[i].fid.Vnode = htonl(fid.Vnode);
532 page->entry[i].fid.Unique = htonl(fid.Unique);
533 strcpy (page->entry[i].name, filename);
534 if (raw_name) {
535 DirEntry2 *entry2;
536 size_t fnl2, fnl = strlen(filename) + 1;
538 page->entry[i].flag |= AFSDIR_UTFENT;
540 if (fnl % 4)
541 fnl2 = fnl + (4 - (fnl % 4));
542 else
543 fnl2 = fnl;
544 memset(page->entry[i].name + fnl, 0, fnl2 - fnl);
546 page->entry[i].length =
547 (((unsigned char *)&page->entry[i].name) -
548 ((unsigned char *)&page->entry[i]) +
549 fnl2) >> 2;
551 entry2 = (DirEntry2 *)(page->entry[i].name + fnl2);
552 entry2->utf_next = 0;
553 entry2->raw_offset =
554 ((unsigned char *)&page->entry[i].name) -
555 ((unsigned char *)&page->entry[i]) +
556 fnl2 + sizeof(*entry2);
557 entry2->norm_offset = entry2->raw_offset;
558 strcpy((char *)(((unsigned char *)&page->entry[i]) + entry2->raw_offset),
559 raw_name);
561 memset(page->entry[i + j - 1].fill, 0, 4);
562 if (pageno < MAXPAGES)
563 page0->dheader.map[pageno] -= n;
564 return i;
566 i += j + 1;
568 return -1;
572 * Remove the entry `off' from the page `page' (page number `pageno').
575 static int
576 remove_from_page (DirPage0 *page0,
577 DirPage1 *page,
578 unsigned pageno,
579 unsigned off)
581 DirEntry *entry = &page->entry[off];
582 unsigned n, i;
584 n = 1 + additional_entries (entry->name, NULL, NULL);
586 if (pageno < MAXPAGES)
587 page0->dheader.map[pageno] += n;
589 entry->next = 0;
590 entry->fid.Vnode = 0;
591 entry->fid.Unique = 0;
593 for (i = off + 1; i < off + n + 1; ++i)
594 set_unused (page, i);
595 return 0;
599 * Lookup `name' in the AFS directory identified by `dir' and change the
600 * fid to `fid'.
604 fdir_changefid (fbuf *the_fbuf,
605 const char *name,
606 const VenusFid *file)
608 DirPage0 *page0;
609 unsigned npages;
610 size_t len = fbuf_len(the_fbuf);
611 int ret;
613 page0 = (DirPage0 *)fbuf_buf(the_fbuf);
614 assert (page0);
616 npages = ntohs(page0->header.pg_pgcount);
617 if (npages < len / AFSDIR_PAGESIZE)
618 npages = len / AFSDIR_PAGESIZE;
620 ret = update_fid_by_name (page0, name, file, npages);
622 if (ret == 0)
623 return 0;
624 else
625 return ENOENT;
629 * Create a new directory with only . and ..
633 fdir_mkdir (fbuf *the_fbuf,
634 AFSFid dot,
635 AFSFid dot_dot,
636 int utf8_dir)
638 DirPage0 *page0;
639 DirPage1 *page;
640 int ind;
641 int i;
642 int tmp;
643 int ret;
645 ret = create_new_page (&page, the_fbuf, utf8_dir);
646 if (ret)
647 return ret;
649 page0 = (DirPage0 *)fbuf_buf(the_fbuf);
650 memset (&page0->dheader, 0, sizeof(page0->dheader));
651 tmp = ENTRIESPERPAGE
652 - (sizeof(PageHeader) + sizeof(DirHeader)) / sizeof(DirEntry);
653 if (utf8_dir)
654 tmp -= 32;
655 page0->header.pg_freecount = tmp;
656 page0->dheader.map[0] = tmp;
657 page0->header.pg_pgcount = htons(1);
659 /* set hashtable used */
660 for (i = 0; i < 13; ++i)
661 set_used (page, i);
663 if (utf8_dir) {
664 /* set hashtable_utf used */
665 for (; i < 13 + 32; ++i)
666 set_used (page, i);
669 assert (page0->dheader.hash[hashentry(".")] == 0);
671 ind = add_to_page (page0, page, 0, ".", NULL, dot, 0);
673 assert (ind >= 0);
675 page0->dheader.hash[hashentry(".")] = htons(ind + 1);
677 assert (page0->dheader.hash[hashentry("..")] == 0);
679 ind = add_to_page (page0, page, 0, "..", NULL, dot_dot, 0);
681 assert (ind >= 0);
683 page0->dheader.hash[hashentry("..")] = htons(ind + 1);
685 return 0;
689 * Create a new entry with name `filename' and contents `fid' in `dir'.
693 fdir_creat (fbuf *dir,
694 const char *name,
695 const char *utf8name,
696 AFSFid fid)
698 int ret;
699 int i;
700 size_t npages;
701 DirPage0 *page0;
702 DirPage1 *page;
703 int ind = 0;
704 unsigned hash_value, next;
705 int utf8_dir, convert_dir = 0;
707 if (fbuf_len(dir) == 0)
708 return EINVAL;
710 page0 = (DirPage0 *)fbuf_buf(dir);
711 assert (page0);
713 npages = ntohs(page0->header.pg_pgcount);
714 if (npages < fbuf_len(dir) / AFSDIR_PAGESIZE)
715 npages = fbuf_len(dir) / AFSDIR_PAGESIZE;
717 if (find_entry (page0, name, npages))
718 return EEXIST;
720 if (page0->header.pg_tag == htons(AFSDIRMAGIC_UTF8))
721 utf8_dir = 1;
722 else if (utf8name) {
723 convert_dir = 1;
724 utf8_dir = 1;
725 utf8name = NULL;
726 } else
727 utf8_dir = 0;
729 hash_value = hashentry (name);
730 next = page0->dheader.hash[hash_value];
732 for (i = 0; i < npages; ++i) {
733 page = getpage (page0, i);
734 ind = add_to_page (page0, page, i, name, utf8name, fid, next);
735 if (ind >= 0)
736 break;
738 if (i == npages) {
739 ret = create_new_page (&page, dir, utf8_dir);
740 if (ret)
741 return ret;
742 page0 = (DirPage0 *)fbuf_buf(dir);
743 page0->header.pg_pgcount = htons(npages + 1);
744 if (i < MAXPAGES)
745 page0->dheader.map[i] = ENTRIESPERPAGE - 1;
746 ind = add_to_page (page0, page, i, name, utf8name, fid, next);
747 assert (ind >= 0);
749 ind += i * ENTRIESPERPAGE;
751 page0->dheader.hash[hash_value] = htons(ind + 1);
753 return 0;
757 * Remove the entry named `name' in dir.
761 fdir_remove (fbuf *dir,
762 const char *name,
763 AFSFid *fid)
765 int i;
766 unsigned len = fbuf_len(dir);
767 DirPage0 *page0;
768 DirPage1 *page;
769 unsigned hash_value;
770 DirEntry *entry = NULL;
771 DirEntry *prev_entry;
772 unsigned pageno;
773 int found;
774 size_t npages;
777 page0 = (DirPage0 *)fbuf_buf(dir);
779 npages = ntohs(page0->header.pg_pgcount);
780 if (npages < len / AFSDIR_PAGESIZE)
781 npages = len / AFSDIR_PAGESIZE;
783 hash_value = hashentry (name);
784 i = ntohs(page0->dheader.hash[hash_value]);
785 found = i == 0;
786 prev_entry = NULL;
787 while (!found) {
788 entry = getentry (page0, i - 1, npages);
789 if (entry == NULL)
790 return ENOENT;
792 if (strcmp (entry->name, name) == 0) {
793 found = TRUE;
794 } else {
795 i = ntohs(entry->next);
796 if (i == 0)
797 found = TRUE;
798 prev_entry = entry;
801 if (i == 0)
802 return ENOENT;
803 else {
804 if (fid) {
805 fid->Vnode = ntohl(entry->fid.Vnode);
806 fid->Unique = ntohl(entry->fid.Unique);
809 if (prev_entry == NULL)
810 page0->dheader.hash[hash_value] = entry->next;
811 else
812 prev_entry->next = entry->next;
814 pageno = (i - 1) / ENTRIESPERPAGE;
815 page = getpage (page0, pageno);
816 remove_from_page (page0, page, pageno, (i - 1) % ENTRIESPERPAGE);
817 if (pageno == npages - 1
818 && is_page_empty (page0, pageno)) {
819 do {
820 len -= AFSDIR_PAGESIZE;
821 --pageno;
822 --npages;
823 } while(is_page_empty(page0, pageno));
824 page0->header.pg_pgcount = htons(npages);
825 fbuf_truncate (dir, len);
827 return 0;
831 #endif /* _KERNEL */