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