LINUX: afs_create infinite fetchStatus loop
[pkg-k5-afs_openafs.git] / src / dir / dir.c
blob6db96b242696f9a196388fcebf61c866dfbe36c3
1 /*
2 * Copyright 2000, International Business Machines Corporation and others.
3 * All Rights Reserved.
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
8 */
10 #include <afsconfig.h>
11 #include <afs/param.h>
13 #ifdef KERNEL
14 # if !defined(UKERNEL)
15 # include "h/types.h"
16 # if !defined(AFS_LINUX26_ENV)
17 # include "h/param.h"
18 # endif
19 # ifdef AFS_AUX_ENV
20 # include "h/mmu.h"
21 # include "h/seg.h"
22 # include "h/sysmacros.h"
23 # include "h/signal.h"
24 # include "h/errno.h"
25 # endif
26 # include "h/time.h"
27 # if defined(AFS_AIX_ENV) || defined(AFS_SGI_ENV) || defined(AFS_SUN5_ENV) || defined(AFS_LINUX20_ENV)
28 # include "h/errno.h"
29 # else
30 # if !defined(AFS_SUN5_ENV) && !defined(AFS_LINUX20_ENV)
31 # include "h/kernel.h"
32 # endif
33 # endif
34 # if defined(AFS_SUN5_ENV) || defined(AFS_HPUX_ENV) || defined(AFS_FBSD_ENV) || defined(AFS_DARWIN80_ENV)
35 # include "afs/sysincludes.h"
36 # endif
37 # if !defined(AFS_SGI64_ENV) && !defined(AFS_DARWIN_ENV) && !defined(AFS_OBSD48_ENV) && !defined(AFS_NBSD_ENV)
38 # include "h/user.h"
39 # endif /* AFS_SGI64_ENV */
40 # include "h/uio.h"
41 # ifdef AFS_OSF_ENV
42 # include <sys/mount.h>
43 # include <sys/vnode.h>
44 # include <ufs/inode.h>
45 # endif
46 # if !defined(AFS_SUN5_ENV) && !defined(AFS_LINUX20_ENV) && !defined(AFS_HPUX110_ENV)
47 # include "h/mbuf.h"
48 # endif
49 # ifndef AFS_LINUX20_ENV
50 # include "netinet/in.h"
51 # endif
52 # else /* !defined(UKERNEL) */
53 # include "afs/stds.h"
54 # include "afs/sysincludes.h"
55 # endif /* !defined(UKERNEL) */
57 /* afs_buffer.c */
58 /* These are needed because afs_prototypes.h is not included here */
59 struct dcache;
60 struct DirBuffer;
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"
66 # include "afs/dir.h"
68 # ifdef AFS_LINUX20_ENV
69 # include "h/string.h"
70 # endif
72 #else /* KERNEL */
74 # include <roken.h>
75 # include "dir.h"
76 #endif /* KERNEL */
78 afs_int32 DErrno;
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 *,
85 struct DirBuffer *);
87 /* Find out how many entries are required to store a name. */
88 int
89 afs_dir_NameBlobs(char *name)
91 int i;
92 i = strlen(name) + 1;
93 return 1 + ((i + 15) >> 5);
96 /* Create an entry in a file. Dir is a file representation, while entry is
97 * a string name. */
98 int
99 afs_dir_Create(dir_file_t dir, char *entry, void *voidfid)
101 afs_int32 *vfid = (afs_int32 *) voidfid;
102 int blobs, firstelt;
103 int i;
104 struct DirBuffer entrybuf, prevbuf, headerbuf;
105 struct DirEntry *ep;
106 struct DirHeader *dhp;
108 /* check name quality */
109 if (*entry == 0)
110 return EINVAL;
112 /* First check if file already exists. */
113 if (FindItem(dir, entry, &prevbuf, &entrybuf) == 0) {
114 DRelease(&entrybuf, 0);
115 DRelease(&prevbuf, 0);
116 return EEXIST;
119 blobs = afs_dir_NameBlobs(entry); /* number of entries required */
120 firstelt = FindBlobs(dir, blobs);
121 if (firstelt < 0)
122 return EFBIG; /* directory is full */
124 /* First, we fill in the directory entry. */
125 if (afs_dir_GetBlob(dir, firstelt, &entrybuf) != 0)
126 return EIO;
127 ep = (struct DirEntry *)entrybuf.data;
129 ep->flag = FFIRST;
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);
137 return EIO;
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);
146 return 0;
150 afs_dir_Length(dir_file_t dir)
152 int i, ctr;
153 struct DirBuffer headerbuf;
154 struct DirHeader *dhp;
156 if (DRead(dir, 0, &headerbuf) != 0)
157 return 0;
158 dhp = (struct DirHeader *)headerbuf.data;
160 if (dhp->header.pgcount != 0)
161 ctr = ntohs(dhp->header.pgcount);
162 else {
163 /* old style, count the pages */
164 ctr = 0;
165 for (i = 0; i < MAXPAGES; i++)
166 if (dhp->alloMap[i] != EPP)
167 ctr++;
169 DRelease(&headerbuf, 0);
170 return ctr * AFS_PAGESIZE;
173 /* Delete an entry from a directory, including update of all free entry
174 * descriptors. */
176 afs_dir_Delete(dir_file_t dir, char *entry)
179 int nitems, index;
180 struct DirBuffer entrybuf, prevbuf;
181 struct DirEntry *firstitem;
182 unsigned short *previtem;
184 if (FindItem(dir, entry, &prevbuf, &entrybuf) != 0)
185 return ENOENT;
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);
198 return 0;
201 /* Find a bunch of contiguous entries; at least nblobs in a row. */
202 static int
203 FindBlobs(dir_file_t dir, int nblobs)
205 int i, j, k;
206 int failed = 0;
207 struct DirBuffer headerbuf, pagebuf;
208 struct DirHeader *dhp;
209 struct PageHeader *pp;
210 int pgcount;
212 /* read the dir header in first. */
213 if (DRead(dir, 0, &headerbuf) != 0)
214 return -1;
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. */
221 if (i >= MAXPAGES) {
222 /* this pages exists past the end of the old-style dir */
223 pgcount = ntohs(dhp->header.pgcount);
224 if (pgcount == 0) {
225 pgcount = MAXPAGES;
226 dhp->header.pgcount = htons(pgcount);
228 if (i > pgcount - 1) {
229 /* this page is bigger than last allocated page */
230 AddPage(dir, i);
231 dhp->header.pgcount = htons(i + 1);
233 } else if (dhp->alloMap[i] == EPP) {
234 /* Add the page to the directory. */
235 AddPage(dir, i);
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) {
242 break;
244 pp = (struct PageHeader *)pagebuf.data;
245 for (j = 0; j <= EPP - nblobs; j++) {
246 failed = 0;
247 for (k = 0; k < nblobs; k++)
248 if ((pp->freebitmap[(j + k) >> 3] >> ((j + k) & 7)) & 1) {
249 failed = 1;
250 break;
252 if (!failed)
253 break;
254 failed = 1;
256 if (!failed) {
257 /* Here we have the first index in j. We update the allocation maps
258 * and free up any resources we've got allocated. */
259 if (i < MAXPAGES)
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);
265 return j + i * EPP;
267 DRelease(&pagebuf, 0); /* This dir page is unchanged. */
270 /* If we make it here, the directory is full. */
271 DRelease(&headerbuf, 1);
272 return -1;
275 static void
276 AddPage(dir_file_t dir, int pageno)
277 { /* Add a page to a directory. */
278 int i;
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);
287 if (pageno > 0)
288 pp->pgcount = 0;
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. */
298 static void
299 FreeBlobs(dir_file_t dir, int firstblob, int nblobs)
301 int i;
302 int page;
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)
310 return;
311 dhp = (struct DirHeader *)headerbuf.data;
313 if (page < MAXPAGES)
314 dhp->alloMap[page] += nblobs;
316 DRelease(&headerbuf, 1);
318 if (DRead(dir, page, &pagehdbuf) != 0)
319 return;
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)
336 int i;
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 .. */
358 return 0;
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)
371 return ENOENT;
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);
378 return 0;
381 /* Look up a file name in directory. */
384 afs_dir_LookupOffset(dir_file_t dir, char *entry, void *voidfid,
385 long *offsetp)
387 afs_int32 *fid = (afs_int32 *) voidfid;
388 struct DirBuffer firstbuf, prevbuf;
389 struct DirEntry *firstitem;
391 if (FindItem(dir, entry, &prevbuf, &firstbuf) != 0)
392 return ENOENT;
393 DRelease(&prevbuf, 0);
394 firstitem = (struct DirEntry *)firstbuf.data;
396 fid[1] = ntohl(firstitem->fid.vnode);
397 fid[2] = ntohl(firstitem->fid.vunique);
398 if (offsetp)
399 *offsetp = DVOffset(&firstbuf);
400 DRelease(&firstbuf, 0);
401 return 0;
405 * Enumerate the contents of a directory. Break when hook function
406 * returns non 0.
410 afs_dir_EnumerateDir(dir_file_t dir, int (*proc) (void *, char *name,
411 afs_int32 vnode,
412 afs_int32 unique),
413 void *hook)
415 int i;
416 int num;
417 struct DirBuffer headerbuf, entrybuf;
418 struct DirHeader *dhp;
419 struct DirEntry *ep;
420 int code = 0;
421 int elements;
423 if (DRead(dir, 0, &headerbuf) != 0)
424 return EIO;
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]);
430 elements = 0;
431 while (num != 0 && elements < BIGMAXPAGES * EPP) {
432 elements++;
434 /* Walk down the hash table list. */
435 code = afs_dir_GetVerifiedBlob(dir, num, &entrybuf);
436 if (code)
437 goto out;
439 ep = (struct DirEntry *)entrybuf.data;
440 if (!ep) {
441 DRelease(&entrybuf, 0);
442 break;
445 num = ntohs(ep->next);
446 code = (*proc) (hook, ep->name, ntohl(ep->fid.vnode),
447 ntohl(ep->fid.vunique));
448 DRelease(&entrybuf, 0);
449 if (code)
450 goto out;
454 out:
455 DRelease(&headerbuf, 0);
456 return 0;
460 afs_dir_IsEmpty(dir_file_t dir)
462 /* Enumerate the contents of a directory. */
463 int i;
464 int num;
465 struct DirBuffer headerbuf, entrybuf;
466 struct DirHeader *dhp;
467 struct DirEntry *ep;
468 int elements;
470 if (DRead(dir, 0, &headerbuf) != 0)
471 return 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]);
477 elements = 0;
478 while (num != 0 && elements < BIGMAXPAGES * EPP) {
479 elements++;
480 /* Walk down the hash table list. */
481 if (afs_dir_GetVerifiedBlob(dir, num, &entrybuf) != 0)
482 break;
483 ep = (struct DirEntry *)entrybuf.data;
484 if (strcmp(ep->name, "..") && strcmp(ep->name, ".")) {
485 DRelease(&entrybuf, 0);
486 DRelease(&headerbuf, 0);
487 return 1;
489 num = ntohs(ep->next);
490 DRelease(&entrybuf, 0);
493 DRelease(&headerbuf, 0);
494 return 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
499 * page.
502 static int
503 GetBlobWithLimit(dir_file_t dir, afs_int32 blobno,
504 struct DirBuffer *buffer, afs_size_t *maxlen)
506 afs_size_t pos;
507 int code;
509 *maxlen = 0;
510 memset(buffer, 0, sizeof(struct DirBuffer));
512 code = DRead(dir, blobno >> LEPP, buffer);
513 if (code)
514 return code;
516 pos = 32 * (blobno & (EPP - 1));
518 *maxlen = AFS_PAGESIZE - pos - 1;
520 buffer->data = (void *)(((char *)buffer->data) + pos);
522 return 0;
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
535 * within
539 afs_dir_GetVerifiedBlob(dir_file_t file, afs_int32 blobno,
540 struct DirBuffer *outbuf)
542 struct DirEntry *dir;
543 struct DirBuffer buffer;
544 afs_size_t maxlen;
545 int code;
546 char *cp;
548 code = GetBlobWithLimit(file, blobno, &buffer, &maxlen);
549 if (code)
550 return code;
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++);
558 if (*cp != '\0') {
559 DRelease(&buffer, 0);
560 return EIO;
563 *outbuf = buffer;
564 return 0;
568 afs_dir_DirHash(char *string)
570 /* Hash a string to a number between 0 and NHASHENT. */
571 unsigned char tc;
572 unsigned int hval;
573 int tval;
574 hval = 0;
575 while ((tc = (*string++))) {
576 hval *= 173;
577 hval += tc;
579 tval = hval & (NHASHENT - 1);
580 if (tval == 0)
581 return tval;
582 else if (hval >= 1u<<31)
583 tval = NHASHENT - tval;
584 return 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. */
594 static int
595 FindItem(dir_file_t dir, char *ename, struct DirBuffer *prevbuf,
596 struct DirBuffer *itembuf )
598 int i, code;
599 struct DirBuffer curr, prev;
600 struct DirHeader *dhp;
601 struct DirEntry *tp;
602 int elements;
604 memset(prevbuf, 0, sizeof(struct DirBuffer));
605 memset(itembuf, 0, sizeof(struct DirBuffer));
607 code = DRead(dir, 0, &prev);
608 if (code)
609 return code;
610 dhp = (struct DirHeader *)prev.data;
612 i = afs_dir_DirHash(ename);
613 if (dhp->hashTable[i] == 0) {
614 /* no such entry */
615 DRelease(&prev, 0);
616 return ENOENT;
619 code = afs_dir_GetVerifiedBlob(dir,
620 (u_short) ntohs(dhp->hashTable[i]),
621 &curr);
622 if (code) {
623 DRelease(&prev, 0);
624 return code;
627 prev.data = &(dhp->hashTable[i]);
628 elements = 0;
629 /* Detect circular hash chains. Absolute max size of a directory */
630 while (elements < BIGMAXPAGES * EPP) {
631 elements++;
633 /* Look at each entry on the hash chain */
634 tp = (struct DirEntry *)curr.data;
635 if (!strcmp(ename, tp->name)) {
636 /* Found it! */
637 *prevbuf = prev;
638 *itembuf = curr;
639 return 0;
642 DRelease(&prev, 0);
644 prev = curr;
645 prev.data = &(tp->next);
647 if (tp->next == 0)
648 goto out; /* The end of the line */
650 code = afs_dir_GetVerifiedBlob(dir, (u_short) ntohs(tp->next),
651 &curr);
652 if (code)
653 goto out;
656 out:
657 DRelease(&prev, 0);
658 return ENOENT;
661 static int
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
668 * instead.
670 int i, code;
671 unsigned short next;
672 struct DirBuffer curr, header;
673 struct DirHeader *dhp;
674 struct DirEntry *tp;
675 int elements;
677 memset(itembuf, 0, sizeof(struct DirBuffer));
679 code = DRead(dir, 0, &header);
680 if (code)
681 return code;
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]),
688 &curr);
689 if (code) {
690 DRelease(&header, 0);
691 return code;
693 elements = 0;
694 while(curr.data != NULL && elements < BIGMAXPAGES * EPP) {
695 elements++;
696 tp = (struct DirEntry *)curr.data;
698 if (vnode == ntohl(tp->fid.vnode)
699 && unique == ntohl(tp->fid.vunique)) {
700 DRelease(&header, 0);
701 *itembuf = curr;
702 return 0;
705 next = tp->next;
706 DRelease(&curr, 0);
708 if (next == 0)
709 break;
711 code = afs_dir_GetVerifiedBlob(dir, (u_short)ntohs(next),
712 &curr);
713 if (code) {
714 DRelease(&header, 0);
715 return code;
720 DRelease(&header, 0);
721 return ENOENT;
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;
731 int code = 0;
733 if (FindFid(dir, vnode, unique, &entrybuf) != 0)
734 return ENOENT;
735 entry = (struct DirEntry *)entrybuf.data;
737 if (strlen(entry->name) >= length)
738 code = E2BIG;
739 else
740 strcpy(name, entry->name);
741 DRelease(&entrybuf, 0);
742 return code;
746 * Change an entry fid.
748 * \param dir
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,
756 afs_uint32 *new_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;
763 /* Find entry. */
764 if (FindItem(dir, entry, &prevbuf, &entrybuf) != 0)
765 return ENOENT;
766 firstitem = (struct DirEntry *)entrybuf.data;
767 DRelease(&prevbuf, 1);
769 /* Replace fid. */
770 if (!old_fid ||
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);
780 return 0;