8aa336d53e359a464ecb743d563d9f0d86b57ff5
[openafs.git] / src / dir / dir.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  *
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  */
9
10 #include <afsconfig.h>
11 #include <afs/param.h>
12
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 #  if !defined(AFS_SUN5_ENV) && !defined(AFS_LINUX20_ENV) && !defined(AFS_HPUX110_ENV)
42 #   include "h/mbuf.h"
43 #  endif
44 #  ifndef AFS_LINUX20_ENV
45 #   include "netinet/in.h"
46 #  endif
47 # else /* !defined(UKERNEL) */
48 #  include "afs/stds.h"
49 #  include "afs/sysincludes.h"
50 # endif /* !defined(UKERNEL) */
51
52 /* afs_buffer.c */
53 /* These are needed because afs_prototypes.h is not included here */
54 struct dcache;
55 struct DirBuffer;
56 extern int DRead(struct dcache *adc, int page, struct DirBuffer *);
57 extern int DNew(struct dcache *adc, int page, struct DirBuffer *);
58
59 # include "afs/afs_osi.h"
60
61 # include "afs/dir.h"
62
63 # ifdef AFS_LINUX20_ENV
64 #  include "h/string.h"
65 # endif
66
67 #else /* KERNEL */
68
69 # include <roken.h>
70 # include "dir.h"
71 #endif /* KERNEL */
72
73 afs_int32 DErrno;
74
75 /* Local static prototypes */
76 static int FindBlobs(dir_file_t, int);
77 static void AddPage(dir_file_t, int);
78 static void FreeBlobs(dir_file_t, int, int);
79 static int FindItem(dir_file_t, char *, struct DirBuffer *,
80                     struct DirBuffer *);
81
82 /* Find out how many entries are required to store a name. */
83 int
84 afs_dir_NameBlobs(char *name)
85 {
86     int i;
87     i = strlen(name) + 1;
88     return 1 + ((i + 15) >> 5);
89 }
90
91 /* Create an entry in a file.  Dir is a file representation, while entry is
92  * a string name. */
93 int
94 afs_dir_Create(dir_file_t dir, char *entry, void *voidfid)
95 {
96     afs_int32 *vfid = (afs_int32 *) voidfid;
97     int blobs, firstelt;
98     int i;
99     struct DirBuffer entrybuf, prevbuf, headerbuf;
100     struct DirEntry *ep;
101     struct DirHeader *dhp;
102     int code;
103
104     /* check name quality */
105     if (*entry == 0)
106         return EINVAL;
107
108     /* First check if file already exists. */
109     code = FindItem(dir, entry, &prevbuf, &entrybuf);
110     if (code && code != ENOENT) {
111         return code;
112     }
113     if (code == 0) {
114         DRelease(&entrybuf, 0);
115         DRelease(&prevbuf, 0);
116         return EEXIST;
117     }
118
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 */
123
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;
128
129     ep->flag = FFIRST;
130     ep->fid.vnode = htonl(vfid[1]);
131     ep->fid.vunique = htonl(vfid[2]);
132     strcpy(ep->name, entry);
133
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;
138     }
139     dhp = (struct DirHeader *)headerbuf.data;
140
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;
147 }
148
149 int
150 afs_dir_Length(dir_file_t dir)
151 {
152     int i, ctr;
153     struct DirBuffer headerbuf;
154     struct DirHeader *dhp;
155
156     if (DRead(dir, 0, &headerbuf) != 0)
157         return 0;
158     dhp = (struct DirHeader *)headerbuf.data;
159
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++;
168     }
169     DRelease(&headerbuf, 0);
170     return ctr * AFS_PAGESIZE;
171 }
172
173 /* Delete an entry from a directory, including update of all free entry
174  * descriptors. */
175 int
176 afs_dir_Delete(dir_file_t dir, char *entry)
177 {
178
179     int nitems, index;
180     struct DirBuffer entrybuf, prevbuf;
181     struct DirEntry *firstitem;
182     unsigned short *previtem;
183     int code;
184
185     code = FindItem(dir, entry, &prevbuf, &entrybuf);
186     if (code) {
187         return code;
188     }
189
190     firstitem = (struct DirEntry *)entrybuf.data;
191     previtem = (unsigned short *)prevbuf.data;
192
193     *previtem = firstitem->next;
194     DRelease(&prevbuf, 1);
195     index = DVOffset(&entrybuf) / 32;
196     nitems = afs_dir_NameBlobs(firstitem->name);
197     /* Clear entire DirEntry and any DirXEntry extensions */
198     memset(firstitem, 0, nitems * sizeof(*firstitem));
199     DRelease(&entrybuf, 1);
200     FreeBlobs(dir, index, nitems);
201     return 0;
202 }
203
204 /* Find a bunch of contiguous entries; at least nblobs in a row. */
205 static int
206 FindBlobs(dir_file_t dir, int nblobs)
207 {
208     int i, j, k;
209     int failed = 0;
210     struct DirBuffer headerbuf, pagebuf;
211     struct DirHeader *dhp;
212     struct PageHeader *pp;
213     int pgcount;
214
215     /* read the dir header in first. */
216     if (DRead(dir, 0, &headerbuf) != 0)
217         return -1;
218     dhp = (struct DirHeader *)headerbuf.data;
219
220     for (i = 0; i < BIGMAXPAGES; i++) {
221         if (i >= MAXPAGES || dhp->alloMap[i] >= nblobs) {
222             /* if page could contain enough entries */
223             /* If there are EPP free entries, then the page is not even allocated. */
224             if (i >= MAXPAGES) {
225                 /* this pages exists past the end of the old-style dir */
226                 pgcount = ntohs(dhp->header.pgcount);
227                 if (pgcount == 0) {
228                     pgcount = MAXPAGES;
229                     dhp->header.pgcount = htons(pgcount);
230                 }
231                 if (i > pgcount - 1) {
232                     /* this page is bigger than last allocated page */
233                     AddPage(dir, i);
234                     dhp->header.pgcount = htons(i + 1);
235                 }
236             } else if (dhp->alloMap[i] == EPP) {
237                 /* Add the page to the directory. */
238                 AddPage(dir, i);
239                 dhp->alloMap[i] = EPP - 1;
240                 dhp->header.pgcount = htons(i + 1);
241             }
242
243             /* read the page in. */
244             if (DRead(dir, i, &pagebuf) != 0) {
245                 break;
246             }
247             pp = (struct PageHeader *)pagebuf.data;
248             for (j = 0; j <= EPP - nblobs; j++) {
249                 failed = 0;
250                 for (k = 0; k < nblobs; k++)
251                     if ((pp->freebitmap[(j + k) >> 3] >> ((j + k) & 7)) & 1) {
252                         failed = 1;
253                         break;
254                     }
255                 if (!failed)
256                     break;
257                 failed = 1;
258             }
259             if (!failed) {
260                 /* Here we have the first index in j.  We update the allocation maps
261                  * and free up any resources we've got allocated. */
262                 if (i < MAXPAGES)
263                     dhp->alloMap[i] -= nblobs;
264                 DRelease(&headerbuf, 1);
265                 for (k = 0; k < nblobs; k++)
266                     pp->freebitmap[(j + k) >> 3] |= 1 << ((j + k) & 7);
267                 DRelease(&pagebuf, 1);
268                 return j + i * EPP;
269             }
270             DRelease(&pagebuf, 0);      /* This dir page is unchanged. */
271         }
272     }
273     /* If we make it here, the directory is full. */
274     DRelease(&headerbuf, 1);
275     return -1;
276 }
277
278 static void
279 AddPage(dir_file_t dir, int pageno)
280 {                               /* Add a page to a directory. */
281     int i;
282     struct PageHeader *pp;
283     struct DirBuffer pagebuf;
284
285     /* Get a new buffer labelled dir,pageno */
286     DNew(dir, pageno, &pagebuf);
287     pp = (struct PageHeader *)pagebuf.data;
288
289     pp->tag = htons(1234);
290     if (pageno > 0)
291         pp->pgcount = 0;
292     pp->freecount = EPP - 1;    /* The first dude is already allocated */
293     pp->freebitmap[0] = 0x01;
294     for (i = 1; i < EPP / 8; i++)       /* It's a constant */
295         pp->freebitmap[i] = 0;
296     DRelease(&pagebuf, 1);
297 }
298
299 /* Free a whole bunch of directory entries. */
300
301 static void
302 FreeBlobs(dir_file_t dir, int firstblob, int nblobs)
303 {
304     int i;
305     int page;
306     struct DirBuffer headerbuf, pagehdbuf;
307     struct DirHeader *dhp;
308     struct PageHeader *pp;
309     page = firstblob / EPP;
310     firstblob -= EPP * page;    /* convert to page-relative entry */
311
312     if (DRead(dir, 0, &headerbuf) != 0)
313         return;
314     dhp = (struct DirHeader *)headerbuf.data;
315
316     if (page < MAXPAGES)
317         dhp->alloMap[page] += nblobs;
318
319     DRelease(&headerbuf, 1);
320
321     if (DRead(dir, page, &pagehdbuf) != 0)
322         return;
323     pp = (struct PageHeader *)pagehdbuf.data;
324
325     for (i = 0; i < nblobs; i++)
326         pp->freebitmap[(firstblob + i) >> 3] &= ~(1 << ((firstblob + i) & 7));
327
328     DRelease(&pagehdbuf, 1);
329 }
330
331 /*
332  * Format an empty directory properly.  Note that the first 13 entries in a
333  * directory header page are allocated, 1 to the page header, 4 to the
334  * allocation map and 8 to the hash table.
335  */
336 int
337 afs_dir_MakeDir(dir_file_t dir, afs_int32 * me, afs_int32 * parent)
338 {
339     int i;
340     struct DirBuffer buffer;
341     struct DirHeader *dhp;
342
343     DNew(dir, 0, &buffer);
344     dhp = (struct DirHeader *)buffer.data;
345
346     dhp->header.pgcount = htons(1);
347     dhp->header.tag = htons(1234);
348     dhp->header.freecount = (EPP - DHE - 1);
349     dhp->header.freebitmap[0] = 0xff;
350     dhp->header.freebitmap[1] = 0x1f;
351     for (i = 2; i < EPP / 8; i++)
352         dhp->header.freebitmap[i] = 0;
353     dhp->alloMap[0] = (EPP - DHE - 1);
354     for (i = 1; i < MAXPAGES; i++)
355         dhp->alloMap[i] = EPP;
356     for (i = 0; i < NHASHENT; i++)
357         dhp->hashTable[i] = 0;
358     DRelease(&buffer, 1);
359     afs_dir_Create(dir, ".", me);
360     afs_dir_Create(dir, "..", parent);  /* Virtue is its own .. */
361     return 0;
362 }
363
364 /* Look up a file name in directory. */
365
366 int
367 afs_dir_Lookup(dir_file_t dir, char *entry, void *voidfid)
368 {
369     afs_int32 *fid = (afs_int32 *) voidfid;
370     struct DirBuffer firstbuf, prevbuf;
371     struct DirEntry *firstitem;
372     int code;
373
374     code = FindItem(dir, entry, &prevbuf, &firstbuf);
375     if (code) {
376         return code;
377     }
378     DRelease(&prevbuf, 0);
379     firstitem = (struct DirEntry *)firstbuf.data;
380
381     fid[1] = ntohl(firstitem->fid.vnode);
382     fid[2] = ntohl(firstitem->fid.vunique);
383     DRelease(&firstbuf, 0);
384     return 0;
385 }
386
387 /* Look up a file name in directory. */
388
389 int
390 afs_dir_LookupOffset(dir_file_t dir, char *entry, void *voidfid,
391                      long *offsetp)
392 {
393     afs_int32 *fid = (afs_int32 *) voidfid;
394     struct DirBuffer firstbuf, prevbuf;
395     struct DirEntry *firstitem;
396     int code;
397
398     code = FindItem(dir, entry, &prevbuf, &firstbuf);
399     if (code) {
400         return code;
401     }
402     DRelease(&prevbuf, 0);
403     firstitem = (struct DirEntry *)firstbuf.data;
404
405     fid[1] = ntohl(firstitem->fid.vnode);
406     fid[2] = ntohl(firstitem->fid.vunique);
407     if (offsetp)
408         *offsetp = DVOffset(&firstbuf);
409     DRelease(&firstbuf, 0);
410     return 0;
411 }
412
413 /*
414  * Enumerate the contents of a directory. Break when hook function
415  * returns non 0.
416  */
417
418 int
419 afs_dir_EnumerateDir(dir_file_t dir, int (*proc) (void *, char *name,
420                                                   afs_int32 vnode,
421                                                   afs_int32 unique),
422                      void *hook)
423 {
424     int i;
425     int num;
426     struct DirBuffer headerbuf, entrybuf;
427     struct DirHeader *dhp;
428     struct DirEntry *ep;
429     int code = 0;
430     int elements;
431
432     if (DRead(dir, 0, &headerbuf) != 0)
433         return EIO;
434     dhp = (struct DirHeader *)headerbuf.data;
435
436     for (i = 0; i < NHASHENT; i++) {
437         /* For each hash chain, enumerate everyone on the list. */
438         num = ntohs(dhp->hashTable[i]);
439         elements = 0;
440         while (num != 0 && elements < BIGMAXPAGES * EPP) {
441             elements++;
442
443             /* Walk down the hash table list. */
444             code = afs_dir_GetVerifiedBlob(dir, num, &entrybuf);
445             if (code)
446                 goto out;
447
448             ep = (struct DirEntry *)entrybuf.data;
449             if (!ep) {
450                 DRelease(&entrybuf, 0);
451                 break;
452             }
453
454             num = ntohs(ep->next);
455             code = (*proc) (hook, ep->name, ntohl(ep->fid.vnode),
456                             ntohl(ep->fid.vunique));
457             DRelease(&entrybuf, 0);
458             if (code)
459                 goto out;
460         }
461     }
462
463 out:
464     DRelease(&headerbuf, 0);
465     return 0;
466 }
467
468 int
469 afs_dir_IsEmpty(dir_file_t dir)
470 {
471     /* Enumerate the contents of a directory. */
472     int i;
473     int num;
474     struct DirBuffer headerbuf, entrybuf;
475     struct DirHeader *dhp;
476     struct DirEntry *ep;
477     int elements;
478
479     if (DRead(dir, 0, &headerbuf) != 0)
480         return 0;
481     dhp = (struct DirHeader *)headerbuf.data;
482
483     for (i = 0; i < NHASHENT; i++) {
484         /* For each hash chain, enumerate everyone on the list. */
485         num = ntohs(dhp->hashTable[i]);
486         elements = 0;
487         while (num != 0 && elements < BIGMAXPAGES * EPP) {
488             elements++;
489             /* Walk down the hash table list. */
490             if (afs_dir_GetVerifiedBlob(dir, num, &entrybuf) != 0)
491                 break;
492             ep = (struct DirEntry *)entrybuf.data;
493             if (strcmp(ep->name, "..") && strcmp(ep->name, ".")) {
494                 DRelease(&entrybuf, 0);
495                 DRelease(&headerbuf, 0);
496                 return 1;
497             }
498             num = ntohs(ep->next);
499             DRelease(&entrybuf, 0);
500         }
501     }
502     DRelease(&headerbuf, 0);
503     return 0;
504 }
505
506 /* Return a pointer to an entry, given its number. Also return the maximum
507  * size of the entry, which is determined by its position within the directory
508  * page.
509  */
510
511 static int
512 GetBlobWithLimit(dir_file_t dir, afs_int32 blobno,
513                  struct DirBuffer *buffer, afs_size_t *maxlen)
514 {
515     afs_size_t pos;
516     int code;
517
518     *maxlen = 0;
519     memset(buffer, 0, sizeof(struct DirBuffer));
520
521     code = DRead(dir, blobno >> LEPP, buffer);
522     if (code)
523         return code;
524
525     pos = 32 * (blobno & (EPP - 1));
526
527     *maxlen = AFS_PAGESIZE - pos - 1;
528
529     buffer->data = (void *)(((char *)buffer->data) + pos);
530
531     return 0;
532 }
533
534 /* Given an entries number, return a pointer to that entry */
535 int
536 afs_dir_GetBlob(dir_file_t dir, afs_int32 blobno, struct DirBuffer *buffer)
537 {
538     afs_size_t maxlen = 0;
539     return GetBlobWithLimit(dir, blobno, buffer, &maxlen);
540 }
541
542 /* Return an entry, having verified that the name held within the entry
543  * doesn't overflow off the end of the directory page it is contained
544  * within
545  */
546
547 int
548 afs_dir_GetVerifiedBlob(dir_file_t file, afs_int32 blobno,
549                         struct DirBuffer *outbuf)
550 {
551     struct DirEntry *dir;
552     struct DirBuffer buffer;
553     afs_size_t maxlen;
554     int code;
555     char *cp;
556
557     code = GetBlobWithLimit(file, blobno, &buffer, &maxlen);
558     if (code)
559         return code;
560
561     dir = (struct DirEntry *)buffer.data;
562
563     /* A blob is only valid if the name within it is NULL terminated before
564      * the end of the blob's containing page */
565     for (cp = dir->name; *cp != '\0' && cp < ((char *)dir) + maxlen; cp++);
566
567     if (*cp != '\0') {
568         DRelease(&buffer, 0);
569         return EIO;
570     }
571
572     *outbuf = buffer;
573     return 0;
574 }
575
576 int
577 afs_dir_DirHash(char *string)
578 {
579     /* Hash a string to a number between 0 and NHASHENT. */
580     unsigned char tc;
581     unsigned int hval;
582     int tval;
583     hval = 0;
584     while ((tc = (*string++))) {
585         hval *= 173;
586         hval += tc;
587     }
588     tval = hval & (NHASHENT - 1);
589     if (tval == 0)
590         return tval;
591     else if (hval >= 1u<<31)
592         tval = NHASHENT - tval;
593     return tval;
594 }
595
596
597 /* Find a directory entry, given its name.  This entry returns a pointer
598  * to a locked buffer, and a pointer to a locked buffer (in previtem)
599  * referencing the found item (to aid the delete code).  If no entry is
600  * found, however, no items are left locked, and a null pointer is
601  * returned instead. */
602
603 static int
604 FindItem(dir_file_t dir, char *ename, struct DirBuffer *prevbuf,
605          struct DirBuffer *itembuf )
606 {
607     int i, code;
608     struct DirBuffer curr, prev;
609     struct DirHeader *dhp;
610     struct DirEntry *tp;
611     int elements;
612
613     memset(prevbuf, 0, sizeof(struct DirBuffer));
614     memset(itembuf, 0, sizeof(struct DirBuffer));
615
616     code = DRead(dir, 0, &prev);
617     if (code)
618         return code;
619     dhp = (struct DirHeader *)prev.data;
620
621     i = afs_dir_DirHash(ename);
622     if (dhp->hashTable[i] == 0) {
623         /* no such entry */
624         code = ENOENT;
625         goto out;
626     }
627
628     code = afs_dir_GetVerifiedBlob(dir,
629                                    (u_short) ntohs(dhp->hashTable[i]),
630                                    &curr);
631     if (code) {
632         goto out;
633     }
634
635     prev.data = &(dhp->hashTable[i]);
636     elements = 0;
637     /* Detect circular hash chains. Absolute max size of a directory */
638     while (elements < BIGMAXPAGES * EPP) {
639         elements++;
640
641         /* Look at each entry on the hash chain */
642         tp = (struct DirEntry *)curr.data;
643         if (!strcmp(ename, tp->name)) {
644             /* Found it! */
645             *prevbuf = prev;
646             *itembuf = curr;
647             return 0;
648         }
649
650         DRelease(&prev, 0);
651
652         prev = curr;
653         prev.data = &(tp->next);
654
655         if (tp->next == 0) {
656             /* The end of the line */
657             code = ENOENT;
658             goto out;
659         }
660
661         code = afs_dir_GetVerifiedBlob(dir, (u_short) ntohs(tp->next),
662                                        &curr);
663         if (code)
664             goto out;
665     }
666
667     /* If we've reached here, we've hit our loop limit. Something is weird with
668      * the directory; maybe a circular hash chain? */
669     code = EIO;
670
671 out:
672     DRelease(&prev, 0);
673     return code;
674 }
675
676 static int
677 FindFid (void *dir, afs_uint32 vnode, afs_uint32 unique,
678          struct DirBuffer *itembuf)
679 {
680     /* Find a directory entry, given the vnode and uniquifier of a object.
681      * This entry returns a pointer to a locked buffer.  If no entry is found,
682      * however, no items are left locked, and a null pointer is returned
683      * instead.
684      */
685     int i, code;
686     unsigned short next;
687     struct DirBuffer curr, header;
688     struct DirHeader *dhp;
689     struct DirEntry *tp;
690     int elements;
691
692     memset(itembuf, 0, sizeof(struct DirBuffer));
693
694     code = DRead(dir, 0, &header);
695     if (code)
696         return code;
697     dhp = (struct DirHeader *)header.data;
698
699     for (i=0; i<NHASHENT; i++) {
700         if (dhp->hashTable[i] != 0) {
701             code = afs_dir_GetVerifiedBlob(dir,
702                                            (u_short)ntohs(dhp->hashTable[i]),
703                                            &curr);
704             if (code) {
705                 DRelease(&header, 0);
706                 return code;
707             }
708             elements = 0;
709             while(curr.data != NULL && elements < BIGMAXPAGES * EPP) {
710                 elements++;
711                 tp = (struct DirEntry *)curr.data;
712
713                 if (vnode == ntohl(tp->fid.vnode)
714                     && unique == ntohl(tp->fid.vunique)) {
715                     DRelease(&header, 0);
716                     *itembuf = curr;
717                     return 0;
718                 }
719
720                 next = tp->next;
721                 DRelease(&curr, 0);
722
723                 if (next == 0)
724                     break;
725
726                 code = afs_dir_GetVerifiedBlob(dir, (u_short)ntohs(next),
727                                                &curr);
728                 if (code) {
729                     DRelease(&header, 0);
730                     return code;
731                 }
732             }
733         }
734     }
735     DRelease(&header, 0);
736     return ENOENT;
737 }
738
739 int
740 afs_dir_InverseLookup(void *dir, afs_uint32 vnode, afs_uint32 unique,
741                       char *name, afs_uint32 length)
742 {
743     /* Look for the name pointing to given vnode and unique in a directory */
744     struct DirBuffer entrybuf;
745     struct DirEntry *entry;
746     int code = 0;
747
748     code = FindFid(dir, vnode, unique, &entrybuf);
749     if (code) {
750         return code;
751     }
752     entry = (struct DirEntry *)entrybuf.data;
753
754     if (strlen(entry->name) >= length)
755         code = E2BIG;
756     else
757         strcpy(name, entry->name);
758     DRelease(&entrybuf, 0);
759     return code;
760 }
761
762 /*!
763  * Change an entry fid.
764  *
765  * \param dir
766  * \param entry The entry name.
767  * \param old_fid The old find in MKFid format (host order).
768  * It can be omitted if you don't need a safety check...
769  * \param new_fid The new find in MKFid format (host order).
770  */
771 int
772 afs_dir_ChangeFid(dir_file_t dir, char *entry, afs_uint32 *old_fid,
773                   afs_uint32 *new_fid)
774 {
775     struct DirBuffer prevbuf, entrybuf;
776     struct DirEntry *firstitem;
777     struct MKFid *fid_old = (struct MKFid *) old_fid;
778     struct MKFid *fid_new = (struct MKFid *) new_fid;
779     int code;
780
781     /* Find entry. */
782     code = FindItem(dir, entry, &prevbuf, &entrybuf);
783     if (code) {
784         return code;
785     }
786     firstitem = (struct DirEntry *)entrybuf.data;
787     DRelease(&prevbuf, 1);
788
789     /* Replace fid. */
790     if (!old_fid ||
791         ((htonl(fid_old->vnode) == firstitem->fid.vnode) &&
792         (htonl(fid_old->vunique) == firstitem->fid.vunique))) {
793
794         firstitem->fid.vnode = htonl(fid_new->vnode);
795         firstitem->fid.vunique = htonl(fid_new->vunique);
796     }
797
798     DRelease(&entrybuf, 1);
799
800     return 0;
801 }