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