2 * Copyright 2000, International Business Machines Corporation and others.
5 * This software has been released under the terms of the IBM Public
6 * License. For details, see the LICENSE file in the top-level source
7 * directory or online at http://www.openafs.org/dl/license10.html
10 #include <afsconfig.h>
11 #include <afs/param.h>
14 # if !defined(UKERNEL)
16 # if !defined(AFS_LINUX26_ENV)
22 # include "h/sysmacros.h"
23 # include "h/signal.h"
27 # if defined(AFS_AIX_ENV) || defined(AFS_SGI_ENV) || defined(AFS_SUN5_ENV) || defined(AFS_LINUX20_ENV)
30 # if !defined(AFS_SUN5_ENV) && !defined(AFS_LINUX20_ENV)
31 # include "h/kernel.h"
34 # if defined(AFS_SUN5_ENV) || defined(AFS_HPUX_ENV) || defined(AFS_FBSD_ENV) || defined(AFS_DARWIN80_ENV)
35 # include "afs/sysincludes.h"
37 # if !defined(AFS_SGI64_ENV) && !defined(AFS_DARWIN_ENV) && !defined(AFS_OBSD48_ENV) && !defined(AFS_NBSD_ENV)
39 # endif /* AFS_SGI64_ENV */
42 # include <sys/mount.h>
43 # include <sys/vnode.h>
44 # include <ufs/inode.h>
46 # if !defined(AFS_SUN5_ENV) && !defined(AFS_LINUX20_ENV) && !defined(AFS_HPUX110_ENV)
49 # ifndef AFS_LINUX20_ENV
50 # include "netinet/in.h"
52 # else /* !defined(UKERNEL) */
53 # include "afs/stds.h"
54 # include "afs/sysincludes.h"
55 # endif /* !defined(UKERNEL) */
58 /* These are needed because afs_prototypes.h is not included here */
61 extern int DRead(struct dcache
*adc
, int page
, struct DirBuffer
*);
62 extern int DNew(struct dcache
*adc
, int page
, struct DirBuffer
*);
64 # include "afs/afs_osi.h"
68 # ifdef AFS_LINUX20_ENV
69 # include "h/string.h"
80 /* Local static prototypes */
81 static int FindBlobs(dir_file_t
, int);
82 static void AddPage(dir_file_t
, int);
83 static void FreeBlobs(dir_file_t
, int, int);
84 static int FindItem(dir_file_t
, char *, struct DirBuffer
*,
87 /* Find out how many entries are required to store a name. */
89 afs_dir_NameBlobs(char *name
)
93 return 1 + ((i
+ 15) >> 5);
96 /* Create an entry in a file. Dir is a file representation, while entry is
99 afs_dir_Create(dir_file_t dir
, char *entry
, void *voidfid
)
101 afs_int32
*vfid
= (afs_int32
*) voidfid
;
104 struct DirBuffer entrybuf
, prevbuf
, headerbuf
;
106 struct DirHeader
*dhp
;
108 /* check name quality */
112 /* First check if file already exists. */
113 if (FindItem(dir
, entry
, &prevbuf
, &entrybuf
) == 0) {
114 DRelease(&entrybuf
, 0);
115 DRelease(&prevbuf
, 0);
119 blobs
= afs_dir_NameBlobs(entry
); /* number of entries required */
120 firstelt
= FindBlobs(dir
, blobs
);
122 return EFBIG
; /* directory is full */
124 /* First, we fill in the directory entry. */
125 if (afs_dir_GetBlob(dir
, firstelt
, &entrybuf
) != 0)
127 ep
= (struct DirEntry
*)entrybuf
.data
;
130 ep
->fid
.vnode
= htonl(vfid
[1]);
131 ep
->fid
.vunique
= htonl(vfid
[2]);
132 strcpy(ep
->name
, entry
);
134 /* Now we just have to thread it on the hash table list. */
135 if (DRead(dir
, 0, &headerbuf
) != 0) {
136 DRelease(&entrybuf
, 1);
139 dhp
= (struct DirHeader
*)headerbuf
.data
;
141 i
= afs_dir_DirHash(entry
);
142 ep
->next
= dhp
->hashTable
[i
];
143 dhp
->hashTable
[i
] = htons(firstelt
);
144 DRelease(&headerbuf
, 1);
145 DRelease(&entrybuf
, 1);
150 afs_dir_Length(dir_file_t dir
)
153 struct DirBuffer headerbuf
;
154 struct DirHeader
*dhp
;
156 if (DRead(dir
, 0, &headerbuf
) != 0)
158 dhp
= (struct DirHeader
*)headerbuf
.data
;
160 if (dhp
->header
.pgcount
!= 0)
161 ctr
= ntohs(dhp
->header
.pgcount
);
163 /* old style, count the pages */
165 for (i
= 0; i
< MAXPAGES
; i
++)
166 if (dhp
->alloMap
[i
] != EPP
)
169 DRelease(&headerbuf
, 0);
170 return ctr
* AFS_PAGESIZE
;
173 /* Delete an entry from a directory, including update of all free entry
176 afs_dir_Delete(dir_file_t dir
, char *entry
)
180 struct DirBuffer entrybuf
, prevbuf
;
181 struct DirEntry
*firstitem
;
182 unsigned short *previtem
;
184 if (FindItem(dir
, entry
, &prevbuf
, &entrybuf
) != 0)
187 firstitem
= (struct DirEntry
*)entrybuf
.data
;
188 previtem
= (unsigned short *)prevbuf
.data
;
190 *previtem
= firstitem
->next
;
191 DRelease(&prevbuf
, 1);
192 index
= DVOffset(&entrybuf
) / 32;
193 nitems
= afs_dir_NameBlobs(firstitem
->name
);
194 /* Clear entire DirEntry and any DirXEntry extensions */
195 memset(firstitem
, 0, nitems
* sizeof(*firstitem
));
196 DRelease(&entrybuf
, 1);
197 FreeBlobs(dir
, index
, nitems
);
201 /* Find a bunch of contiguous entries; at least nblobs in a row. */
203 FindBlobs(dir_file_t dir
, int nblobs
)
207 struct DirBuffer headerbuf
, pagebuf
;
208 struct DirHeader
*dhp
;
209 struct PageHeader
*pp
;
212 /* read the dir header in first. */
213 if (DRead(dir
, 0, &headerbuf
) != 0)
215 dhp
= (struct DirHeader
*)headerbuf
.data
;
217 for (i
= 0; i
< BIGMAXPAGES
; i
++) {
218 if (i
>= MAXPAGES
|| dhp
->alloMap
[i
] >= nblobs
) {
219 /* if page could contain enough entries */
220 /* If there are EPP free entries, then the page is not even allocated. */
222 /* this pages exists past the end of the old-style dir */
223 pgcount
= ntohs(dhp
->header
.pgcount
);
226 dhp
->header
.pgcount
= htons(pgcount
);
228 if (i
> pgcount
- 1) {
229 /* this page is bigger than last allocated page */
231 dhp
->header
.pgcount
= htons(i
+ 1);
233 } else if (dhp
->alloMap
[i
] == EPP
) {
234 /* Add the page to the directory. */
236 dhp
->alloMap
[i
] = EPP
- 1;
237 dhp
->header
.pgcount
= htons(i
+ 1);
240 /* read the page in. */
241 if (DRead(dir
, i
, &pagebuf
) != 0) {
244 pp
= (struct PageHeader
*)pagebuf
.data
;
245 for (j
= 0; j
<= EPP
- nblobs
; j
++) {
247 for (k
= 0; k
< nblobs
; k
++)
248 if ((pp
->freebitmap
[(j
+ k
) >> 3] >> ((j
+ k
) & 7)) & 1) {
257 /* Here we have the first index in j. We update the allocation maps
258 * and free up any resources we've got allocated. */
260 dhp
->alloMap
[i
] -= nblobs
;
261 DRelease(&headerbuf
, 1);
262 for (k
= 0; k
< nblobs
; k
++)
263 pp
->freebitmap
[(j
+ k
) >> 3] |= 1 << ((j
+ k
) & 7);
264 DRelease(&pagebuf
, 1);
267 DRelease(&pagebuf
, 0); /* This dir page is unchanged. */
270 /* If we make it here, the directory is full. */
271 DRelease(&headerbuf
, 1);
276 AddPage(dir_file_t dir
, int pageno
)
277 { /* Add a page to a directory. */
279 struct PageHeader
*pp
;
280 struct DirBuffer pagebuf
;
282 /* Get a new buffer labelled dir,pageno */
283 DNew(dir
, pageno
, &pagebuf
);
284 pp
= (struct PageHeader
*)pagebuf
.data
;
286 pp
->tag
= htons(1234);
289 pp
->freecount
= EPP
- 1; /* The first dude is already allocated */
290 pp
->freebitmap
[0] = 0x01;
291 for (i
= 1; i
< EPP
/ 8; i
++) /* It's a constant */
292 pp
->freebitmap
[i
] = 0;
293 DRelease(&pagebuf
, 1);
296 /* Free a whole bunch of directory entries. */
299 FreeBlobs(dir_file_t dir
, int firstblob
, int nblobs
)
303 struct DirBuffer headerbuf
, pagehdbuf
;
304 struct DirHeader
*dhp
;
305 struct PageHeader
*pp
;
306 page
= firstblob
/ EPP
;
307 firstblob
-= EPP
* page
; /* convert to page-relative entry */
309 if (DRead(dir
, 0, &headerbuf
) != 0)
311 dhp
= (struct DirHeader
*)headerbuf
.data
;
314 dhp
->alloMap
[page
] += nblobs
;
316 DRelease(&headerbuf
, 1);
318 if (DRead(dir
, page
, &pagehdbuf
) != 0)
320 pp
= (struct PageHeader
*)pagehdbuf
.data
;
322 for (i
= 0; i
< nblobs
; i
++)
323 pp
->freebitmap
[(firstblob
+ i
) >> 3] &= ~(1 << ((firstblob
+ i
) & 7));
325 DRelease(&pagehdbuf
, 1);
329 * Format an empty directory properly. Note that the first 13 entries in a
330 * directory header page are allocated, 1 to the page header, 4 to the
331 * allocation map and 8 to the hash table.
334 afs_dir_MakeDir(dir_file_t dir
, afs_int32
* me
, afs_int32
* parent
)
337 struct DirBuffer buffer
;
338 struct DirHeader
*dhp
;
340 DNew(dir
, 0, &buffer
);
341 dhp
= (struct DirHeader
*)buffer
.data
;
343 dhp
->header
.pgcount
= htons(1);
344 dhp
->header
.tag
= htons(1234);
345 dhp
->header
.freecount
= (EPP
- DHE
- 1);
346 dhp
->header
.freebitmap
[0] = 0xff;
347 dhp
->header
.freebitmap
[1] = 0x1f;
348 for (i
= 2; i
< EPP
/ 8; i
++)
349 dhp
->header
.freebitmap
[i
] = 0;
350 dhp
->alloMap
[0] = (EPP
- DHE
- 1);
351 for (i
= 1; i
< MAXPAGES
; i
++)
352 dhp
->alloMap
[i
] = EPP
;
353 for (i
= 0; i
< NHASHENT
; i
++)
354 dhp
->hashTable
[i
] = 0;
355 DRelease(&buffer
, 1);
356 afs_dir_Create(dir
, ".", me
);
357 afs_dir_Create(dir
, "..", parent
); /* Virtue is its own .. */
361 /* Look up a file name in directory. */
364 afs_dir_Lookup(dir_file_t dir
, char *entry
, void *voidfid
)
366 afs_int32
*fid
= (afs_int32
*) voidfid
;
367 struct DirBuffer firstbuf
, prevbuf
;
368 struct DirEntry
*firstitem
;
370 if (FindItem(dir
, entry
, &prevbuf
, &firstbuf
) != 0)
372 DRelease(&prevbuf
, 0);
373 firstitem
= (struct DirEntry
*)firstbuf
.data
;
375 fid
[1] = ntohl(firstitem
->fid
.vnode
);
376 fid
[2] = ntohl(firstitem
->fid
.vunique
);
377 DRelease(&firstbuf
, 0);
381 /* Look up a file name in directory. */
384 afs_dir_LookupOffset(dir_file_t dir
, char *entry
, void *voidfid
,
387 afs_int32
*fid
= (afs_int32
*) voidfid
;
388 struct DirBuffer firstbuf
, prevbuf
;
389 struct DirEntry
*firstitem
;
391 if (FindItem(dir
, entry
, &prevbuf
, &firstbuf
) != 0)
393 DRelease(&prevbuf
, 0);
394 firstitem
= (struct DirEntry
*)firstbuf
.data
;
396 fid
[1] = ntohl(firstitem
->fid
.vnode
);
397 fid
[2] = ntohl(firstitem
->fid
.vunique
);
399 *offsetp
= DVOffset(&firstbuf
);
400 DRelease(&firstbuf
, 0);
405 * Enumerate the contents of a directory. Break when hook function
410 afs_dir_EnumerateDir(dir_file_t dir
, int (*proc
) (void *, char *name
,
417 struct DirBuffer headerbuf
, entrybuf
;
418 struct DirHeader
*dhp
;
423 if (DRead(dir
, 0, &headerbuf
) != 0)
425 dhp
= (struct DirHeader
*)headerbuf
.data
;
427 for (i
= 0; i
< NHASHENT
; i
++) {
428 /* For each hash chain, enumerate everyone on the list. */
429 num
= ntohs(dhp
->hashTable
[i
]);
431 while (num
!= 0 && elements
< BIGMAXPAGES
* EPP
) {
434 /* Walk down the hash table list. */
435 code
= afs_dir_GetVerifiedBlob(dir
, num
, &entrybuf
);
439 ep
= (struct DirEntry
*)entrybuf
.data
;
441 DRelease(&entrybuf
, 0);
445 num
= ntohs(ep
->next
);
446 code
= (*proc
) (hook
, ep
->name
, ntohl(ep
->fid
.vnode
),
447 ntohl(ep
->fid
.vunique
));
448 DRelease(&entrybuf
, 0);
455 DRelease(&headerbuf
, 0);
460 afs_dir_IsEmpty(dir_file_t dir
)
462 /* Enumerate the contents of a directory. */
465 struct DirBuffer headerbuf
, entrybuf
;
466 struct DirHeader
*dhp
;
470 if (DRead(dir
, 0, &headerbuf
) != 0)
472 dhp
= (struct DirHeader
*)headerbuf
.data
;
474 for (i
= 0; i
< NHASHENT
; i
++) {
475 /* For each hash chain, enumerate everyone on the list. */
476 num
= ntohs(dhp
->hashTable
[i
]);
478 while (num
!= 0 && elements
< BIGMAXPAGES
* EPP
) {
480 /* Walk down the hash table list. */
481 if (afs_dir_GetVerifiedBlob(dir
, num
, &entrybuf
) != 0)
483 ep
= (struct DirEntry
*)entrybuf
.data
;
484 if (strcmp(ep
->name
, "..") && strcmp(ep
->name
, ".")) {
485 DRelease(&entrybuf
, 0);
486 DRelease(&headerbuf
, 0);
489 num
= ntohs(ep
->next
);
490 DRelease(&entrybuf
, 0);
493 DRelease(&headerbuf
, 0);
497 /* Return a pointer to an entry, given its number. Also return the maximum
498 * size of the entry, which is determined by its position within the directory
503 GetBlobWithLimit(dir_file_t dir
, afs_int32 blobno
,
504 struct DirBuffer
*buffer
, afs_size_t
*maxlen
)
510 memset(buffer
, 0, sizeof(struct DirBuffer
));
512 code
= DRead(dir
, blobno
>> LEPP
, buffer
);
516 pos
= 32 * (blobno
& (EPP
- 1));
518 *maxlen
= AFS_PAGESIZE
- pos
- 1;
520 buffer
->data
= (void *)(((char *)buffer
->data
) + pos
);
525 /* Given an entries number, return a pointer to that entry */
527 afs_dir_GetBlob(dir_file_t dir
, afs_int32 blobno
, struct DirBuffer
*buffer
)
529 afs_size_t maxlen
= 0;
530 return GetBlobWithLimit(dir
, blobno
, buffer
, &maxlen
);
533 /* Return an entry, having verified that the name held within the entry
534 * doesn't overflow off the end of the directory page it is contained
539 afs_dir_GetVerifiedBlob(dir_file_t file
, afs_int32 blobno
,
540 struct DirBuffer
*outbuf
)
542 struct DirEntry
*dir
;
543 struct DirBuffer buffer
;
548 code
= GetBlobWithLimit(file
, blobno
, &buffer
, &maxlen
);
552 dir
= (struct DirEntry
*)buffer
.data
;
554 /* A blob is only valid if the name within it is NULL terminated before
555 * the end of the blob's containing page */
556 for (cp
= dir
->name
; *cp
!= '\0' && cp
< ((char *)dir
) + maxlen
; cp
++);
559 DRelease(&buffer
, 0);
568 afs_dir_DirHash(char *string
)
570 /* Hash a string to a number between 0 and NHASHENT. */
575 while ((tc
= (*string
++))) {
579 tval
= hval
& (NHASHENT
- 1);
582 else if (hval
>= 1u<<31)
583 tval
= NHASHENT
- tval
;
588 /* Find a directory entry, given its name. This entry returns a pointer
589 * to a locked buffer, and a pointer to a locked buffer (in previtem)
590 * referencing the found item (to aid the delete code). If no entry is
591 * found, however, no items are left locked, and a null pointer is
592 * returned instead. */
595 FindItem(dir_file_t dir
, char *ename
, struct DirBuffer
*prevbuf
,
596 struct DirBuffer
*itembuf
)
599 struct DirBuffer curr
, prev
;
600 struct DirHeader
*dhp
;
604 memset(prevbuf
, 0, sizeof(struct DirBuffer
));
605 memset(itembuf
, 0, sizeof(struct DirBuffer
));
607 code
= DRead(dir
, 0, &prev
);
610 dhp
= (struct DirHeader
*)prev
.data
;
612 i
= afs_dir_DirHash(ename
);
613 if (dhp
->hashTable
[i
] == 0) {
619 code
= afs_dir_GetVerifiedBlob(dir
,
620 (u_short
) ntohs(dhp
->hashTable
[i
]),
627 prev
.data
= &(dhp
->hashTable
[i
]);
629 /* Detect circular hash chains. Absolute max size of a directory */
630 while (elements
< BIGMAXPAGES
* EPP
) {
633 /* Look at each entry on the hash chain */
634 tp
= (struct DirEntry
*)curr
.data
;
635 if (!strcmp(ename
, tp
->name
)) {
645 prev
.data
= &(tp
->next
);
648 goto out
; /* The end of the line */
650 code
= afs_dir_GetVerifiedBlob(dir
, (u_short
) ntohs(tp
->next
),
662 FindFid (void *dir
, afs_uint32 vnode
, afs_uint32 unique
,
663 struct DirBuffer
*itembuf
)
665 /* Find a directory entry, given the vnode and uniquifier of a object.
666 * This entry returns a pointer to a locked buffer. If no entry is found,
667 * however, no items are left locked, and a null pointer is returned
672 struct DirBuffer curr
, header
;
673 struct DirHeader
*dhp
;
677 memset(itembuf
, 0, sizeof(struct DirBuffer
));
679 code
= DRead(dir
, 0, &header
);
682 dhp
= (struct DirHeader
*)header
.data
;
684 for (i
=0; i
<NHASHENT
; i
++) {
685 if (dhp
->hashTable
[i
] != 0) {
686 code
= afs_dir_GetVerifiedBlob(dir
,
687 (u_short
)ntohs(dhp
->hashTable
[i
]),
690 DRelease(&header
, 0);
694 while(curr
.data
!= NULL
&& elements
< BIGMAXPAGES
* EPP
) {
696 tp
= (struct DirEntry
*)curr
.data
;
698 if (vnode
== ntohl(tp
->fid
.vnode
)
699 && unique
== ntohl(tp
->fid
.vunique
)) {
700 DRelease(&header
, 0);
711 code
= afs_dir_GetVerifiedBlob(dir
, (u_short
)ntohs(next
),
714 DRelease(&header
, 0);
720 DRelease(&header
, 0);
725 afs_dir_InverseLookup(void *dir
, afs_uint32 vnode
, afs_uint32 unique
,
726 char *name
, afs_uint32 length
)
728 /* Look for the name pointing to given vnode and unique in a directory */
729 struct DirBuffer entrybuf
;
730 struct DirEntry
*entry
;
733 if (FindFid(dir
, vnode
, unique
, &entrybuf
) != 0)
735 entry
= (struct DirEntry
*)entrybuf
.data
;
737 if (strlen(entry
->name
) >= length
)
740 strcpy(name
, entry
->name
);
741 DRelease(&entrybuf
, 0);
746 * Change an entry fid.
749 * \param entry The entry name.
750 * \param old_fid The old find in MKFid format (host order).
751 * It can be omitted if you don't need a safety check...
752 * \param new_fid The new find in MKFid format (host order).
755 afs_dir_ChangeFid(dir_file_t dir
, char *entry
, afs_uint32
*old_fid
,
758 struct DirBuffer prevbuf
, entrybuf
;
759 struct DirEntry
*firstitem
;
760 struct MKFid
*fid_old
= (struct MKFid
*) old_fid
;
761 struct MKFid
*fid_new
= (struct MKFid
*) new_fid
;
764 if (FindItem(dir
, entry
, &prevbuf
, &entrybuf
) != 0)
766 firstitem
= (struct DirEntry
*)entrybuf
.data
;
767 DRelease(&prevbuf
, 1);
771 ((htonl(fid_old
->vnode
) == firstitem
->fid
.vnode
) &&
772 (htonl(fid_old
->vunique
) == firstitem
->fid
.vunique
))) {
774 firstitem
->fid
.vnode
= htonl(fid_new
->vnode
);
775 firstitem
->fid
.vunique
= htonl(fid_new
->vunique
);
778 DRelease(&entrybuf
, 1);