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