2 * Copyright (c) 1995 - 2006 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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
35 * Routines for reading an AFS directory
43 #include <sys/types.h>
44 #include <sys/errno.h>
50 #include <nnpfs_locl.h>
57 * Hash the filename of one entry.
61 hashentry (const char *sentry
)
64 const unsigned char *entry
= (const unsigned char *)sentry
;
67 s
= s
* 173 + *entry
++;
68 h
= s
& (ADIRHASHSIZE
- 1);
77 * Return the number of additional DirEntries used by an entry with
78 * the filename `name`.
82 additional_entries (const char *filename
,
86 static DirEntry dummy
;
88 size_t len
= (strlen(filename
) - sizeof(dummy
.name
) + 1
89 + sizeof(DirEntry
) - 1);
91 /* align to 4 byte boundery */
94 len
+= sizeof(DirEntry2
);
95 len
+= strlen(raw_utf
);
97 len
+= strlen(norm_utf
);
99 return len
/ sizeof(DirEntry
);
103 * Return a pointer to page number `pageno'.
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.
118 getentry (DirPage0
*page0
,
123 unsigned idx
= num
/ ENTRIESPERPAGE
;
128 page
= getpage (page0
, idx
);
130 if (page
->header
.pg_tag
!= htons(AFSDIRMAGIC
) &&
131 page
->header
.pg_tag
!= htons(AFSDIRMAGIC_UTF8
))
134 return &page
->entry
[num
% ENTRIESPERPAGE
];
138 * Return a pointer to the entry with name `name' in the directory `page0'.
142 find_entry(DirPage0
*page0
, const char *name
, size_t npages
)
147 for (i
= ntohs(page0
->dheader
.hash
[hashentry (name
)]);
149 i
= ntohs(entry
->next
))
151 entry
= getentry (page0
, (unsigned short)(i
- 1), npages
);
155 if (strcmp (entry
->name
, name
) == 0)
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.
168 find_by_name (DirPage0
*page0
,
174 const DirEntry
*entry
= find_entry (page0
, name
, npages
);
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
);
186 * Return true if slot `off' on `page' is being used.
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.
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
))
211 * Is this page `pageno' empty?
215 is_page_empty (DirPage0
*page0
, unsigned pageno
)
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)
225 for (i
= 1; i
< sizeof(page
->header
.pg_bitmap
); ++i
)
226 if (page
->header
.pg_bitmap
[i
] != 0)
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
)
243 size_t len
= fbuf_len(the_fbuf
);
245 page0
= (DirPage0
*)fbuf_buf(the_fbuf
);
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
);
261 * Return TRUE if dir is empty.
265 fdir_emptyp (fbuf
*dir
)
269 size_t len
= fbuf_len(dir
);
271 page0
= (DirPage0
*)fbuf_buf(dir
);
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
,
296 size_t len
= fbuf_len(the_fbuf
);
300 page0
= (DirPage0
*)fbuf_buf(the_fbuf
);
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)
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
];
327 char *raw_utf
= NULL
;
328 char *norm_utf
= NULL
;
330 if ((entry
->flag
& AFSDIR_FIRST
) == 0)
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
345 if ((unsigned char *)direntry2
>
346 ((unsigned char *)page
) + AFSDIR_PAGESIZE
- sizeof(*direntry2
))
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
)
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
);
383 *offset
= i
* ENTRIESPERPAGE
+ j
;
391 * Return non-zero is `the_fbuf' passes casual inspection as a
396 fdir_dirp (fbuf
*the_fbuf
)
400 unsigned num
, npages
;
401 size_t len
= fbuf_len(the_fbuf
);
403 if (len
< AFSDIR_PAGESIZE
)
406 page0
= (DirPage0
*)fbuf_buf(the_fbuf
);
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
))
425 * Change the fid for `name' to `fid'. Return 0 or -1.
429 update_fid_by_name (DirPage0
*page0
,
434 DirEntry
*entry
= find_entry (page0
, name
, npages
);
439 entry
->fid
.Vnode
= htonl (fid
->fid
.Vnode
);
440 entry
->fid
.Unique
= htonl (fid
->fid
.Unique
);
445 * Mark slot `off' on `page' as being used.
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.
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.
471 create_new_page (DirPage1
**ret_page
,
472 fbuf
*the_fbuf
, int utf8_page
)
476 size_t len
= fbuf_len(the_fbuf
);
478 ret
= fbuf_truncate (the_fbuf
, len
+ AFSDIR_PAGESIZE
);
482 page1
= (DirPage1
*)((char *)fbuf_buf(the_fbuf
) + len
);
483 page1
->header
.pg_pgcount
= htons(0);
485 page1
->header
.pg_tag
= htons(AFSDIRMAGIC_UTF8
);
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
));
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.
503 add_to_page (DirPage0
*page0
,
506 const char *filename
,
507 const char *raw_name
,
514 n
= 1 + additional_entries (filename
, raw_name
, NULL
);
516 if (pageno
< MAXPAGES
&& page0
->dheader
.map
[pageno
] < n
)
519 for (i
= 0; i
< ENTRIESPERPAGE
- n
;) {
520 for (j
= 0; j
< n
&& !used_slot (page
, i
+ j
+ 1); ++j
)
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
);
536 size_t fnl2
, fnl
= strlen(filename
) + 1;
538 page
->entry
[i
].flag
|= AFSDIR_UTFENT
;
541 fnl2
= fnl
+ (4 - (fnl
% 4));
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
]) +
551 entry2
= (DirEntry2
*)(page
->entry
[i
].name
+ fnl2
);
552 entry2
->utf_next
= 0;
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
),
561 memset(page
->entry
[i
+ j
- 1].fill
, 0, 4);
562 if (pageno
< MAXPAGES
)
563 page0
->dheader
.map
[pageno
] -= n
;
572 * Remove the entry `off' from the page `page' (page number `pageno').
576 remove_from_page (DirPage0
*page0
,
581 DirEntry
*entry
= &page
->entry
[off
];
584 n
= 1 + additional_entries (entry
->name
, NULL
, NULL
);
586 if (pageno
< MAXPAGES
)
587 page0
->dheader
.map
[pageno
] += n
;
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
);
599 * Lookup `name' in the AFS directory identified by `dir' and change the
604 fdir_changefid (fbuf
*the_fbuf
,
606 const VenusFid
*file
)
610 size_t len
= fbuf_len(the_fbuf
);
613 page0
= (DirPage0
*)fbuf_buf(the_fbuf
);
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
);
629 * Create a new directory with only . and ..
633 fdir_mkdir (fbuf
*the_fbuf
,
645 ret
= create_new_page (&page
, the_fbuf
, utf8_dir
);
649 page0
= (DirPage0
*)fbuf_buf(the_fbuf
);
650 memset (&page0
->dheader
, 0, sizeof(page0
->dheader
));
652 - (sizeof(PageHeader
) + sizeof(DirHeader
)) / sizeof(DirEntry
);
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
)
664 /* set hashtable_utf used */
665 for (; i
< 13 + 32; ++i
)
669 assert (page0
->dheader
.hash
[hashentry(".")] == 0);
671 ind
= add_to_page (page0
, page
, 0, ".", NULL
, dot
, 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);
683 page0
->dheader
.hash
[hashentry("..")] = htons(ind
+ 1);
689 * Create a new entry with name `filename' and contents `fid' in `dir'.
693 fdir_creat (fbuf
*dir
,
695 const char *utf8name
,
704 unsigned hash_value
, next
;
705 int utf8_dir
, convert_dir
= 0;
707 if (fbuf_len(dir
) == 0)
710 page0
= (DirPage0
*)fbuf_buf(dir
);
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
))
720 if (page0
->header
.pg_tag
== htons(AFSDIRMAGIC_UTF8
))
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
);
739 ret
= create_new_page (&page
, dir
, utf8_dir
);
742 page0
= (DirPage0
*)fbuf_buf(dir
);
743 page0
->header
.pg_pgcount
= htons(npages
+ 1);
745 page0
->dheader
.map
[i
] = ENTRIESPERPAGE
- 1;
746 ind
= add_to_page (page0
, page
, i
, name
, utf8name
, fid
, next
);
749 ind
+= i
* ENTRIESPERPAGE
;
751 page0
->dheader
.hash
[hash_value
] = htons(ind
+ 1);
757 * Remove the entry named `name' in dir.
761 fdir_remove (fbuf
*dir
,
766 unsigned len
= fbuf_len(dir
);
770 DirEntry
*entry
= NULL
;
771 DirEntry
*prev_entry
;
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
]);
788 entry
= getentry (page0
, i
- 1, npages
);
792 if (strcmp (entry
->name
, name
) == 0) {
795 i
= ntohs(entry
->next
);
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
;
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
)) {
820 len
-= AFSDIR_PAGESIZE
;
823 } while(is_page_empty(page0
, pageno
));
824 page0
->header
.pg_pgcount
= htons(npages
);
825 fbuf_truncate (dir
, len
);