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 | /* | |
11 | * afs_vnop_readdir.c - afs_readdir and bulk stat | |
12 | * | |
13 | * Implements: | |
14 | * BlobScan | |
15 | * afs_readdir_move | |
16 | * afs_bulkstat_send | |
17 | * afs_readdir/afs_readdir2(HP) | |
18 | * afs_readdir1 - HP NFS version | |
19 | * | |
20 | */ | |
21 | ||
22 | #include <afsconfig.h> | |
23 | #include "afs/param.h" | |
24 | ||
25 | ||
26 | #include "afs/sysincludes.h" /* Standard vendor system headers */ | |
27 | #include "afsincludes.h" /* Afs-based standard headers */ | |
28 | #include "afs/afs_stats.h" /* statistics */ | |
29 | #include "afs/afs_cbqueue.h" | |
30 | #include "afs/nfsclient.h" | |
31 | #include "afs/afs_osidnlc.h" | |
32 | ||
33 | #if defined(AFS_HPUX1122_ENV) | |
34 | #define DIRPAD 7 | |
35 | #elif defined(AFS_NBSD40_ENV) | |
36 | #define DIRPAD 7 | |
37 | #else | |
38 | #define DIRPAD 3 | |
39 | #endif | |
40 | ||
41 | /* | |
42 | * AFS readdir vnodeop and bulk stat support. | |
43 | */ | |
44 | ||
45 | /** | |
46 | * Ensure that the blob reference refers to a valid directory entry. | |
47 | * It consults the allocation map in the page header to determine | |
48 | * whether a blob is actually in use or not. | |
49 | * | |
50 | * More formally, BlobScan is supposed to return a new blob number | |
51 | * which is just like the input parameter, only it is advanced over | |
52 | * header or free blobs. | |
53 | * | |
54 | * Note that BlobScan switches pages if necessary. BlobScan may | |
55 | * return either 0 for success or an error code. Upon successful | |
56 | * return, the new blob value is assigned to *ablobOut. The new | |
57 | * blob value (*ablobOut) is set to 0 when the end of the file has | |
58 | * been reached. | |
59 | * | |
60 | * BlobScan is used by the Linux port in a separate file, so it should not | |
61 | * become static. | |
62 | */ | |
63 | int | |
64 | BlobScan(struct dcache * afile, afs_int32 ablob, int *ablobOut) | |
65 | { | |
66 | afs_int32 relativeBlob; | |
67 | afs_int32 pageBlob; | |
68 | struct PageHeader *tpe; | |
69 | struct DirBuffer headerbuf; | |
70 | afs_int32 i; | |
71 | int code; | |
72 | ||
73 | AFS_STATCNT(BlobScan); | |
74 | /* advance ablob over free and header blobs */ | |
75 | while (1) { | |
76 | pageBlob = ablob & ~(EPP - 1); /* base blob in same page */ | |
77 | code = afs_dir_GetBlob(afile, pageBlob, &headerbuf); | |
78 | if (code == ENOENT) { | |
79 | *ablobOut = 0; /* past the end of file */ | |
80 | return 0; /* not an error */ | |
81 | } | |
82 | if (code) | |
83 | return code; | |
84 | tpe = (struct PageHeader *)headerbuf.data; | |
85 | ||
86 | relativeBlob = ablob - pageBlob; /* relative to page's first blob */ | |
87 | /* first watch for headers */ | |
88 | if (pageBlob == 0) { /* first dir page has extra-big header */ | |
89 | /* first page */ | |
90 | if (relativeBlob < DHE + 1) | |
91 | relativeBlob = DHE + 1; | |
92 | } else { /* others have one header blob */ | |
93 | if (relativeBlob == 0) | |
94 | relativeBlob = 1; | |
95 | } | |
96 | /* make sure blob is allocated */ | |
97 | for (i = relativeBlob; i < EPP; i++) { | |
98 | if (tpe->freebitmap[i >> 3] & (1 << (i & 7))) | |
99 | break; | |
100 | } | |
101 | /* now relativeBlob is the page-relative first allocated blob, | |
102 | * or EPP (if there are none in this page). */ | |
103 | DRelease(&headerbuf, 0); | |
104 | if (i != EPP) { | |
105 | *ablobOut = i + pageBlob; | |
106 | return 0; | |
107 | } | |
108 | ablob = pageBlob + EPP; /* go around again */ | |
109 | } | |
110 | /* never get here */ | |
111 | } | |
112 | ||
113 | ||
114 | #if !defined(AFS_LINUX20_ENV) | |
115 | /* Changes to afs_readdir which affect dcache or vcache handling or use of | |
116 | * bulk stat data should also be reflected in the Linux specific verison of | |
117 | * the readdir routine. | |
118 | */ | |
119 | ||
120 | /* | |
121 | * The kernel don't like it so much to have large stuff on the stack. | |
122 | * Here we use a watered down version of the direct struct, since | |
123 | * its not too bright to double copy the strings anyway. | |
124 | */ | |
125 | #if !defined(UKERNEL) | |
126 | #if defined(AFS_SGI_ENV) | |
127 | /* Long form for 64 bit apps and kernel requests. */ | |
128 | struct min_dirent { /* miniature dirent structure */ | |
129 | /* If struct dirent changes, this must too */ | |
130 | ino_t d_fileno; /* This is 32 bits for 3.5, 64 for 6.2+ */ | |
131 | off64_t d_off; | |
132 | u_short d_reclen; | |
133 | }; | |
134 | /* Short form for 32 bit apps. */ | |
135 | struct irix5_min_dirent { /* miniature dirent structure */ | |
136 | /* If struct dirent changes, this must too */ | |
137 | afs_uint32 d_fileno; | |
138 | afs_int32 d_off; | |
139 | u_short d_reclen; | |
140 | }; | |
141 | #ifdef AFS_SGI62_ENV | |
142 | #define AFS_DIRENT32BASESIZE IRIX5_DIRENTBASESIZE | |
143 | #define AFS_DIRENT64BASESIZE DIRENT64BASESIZE | |
144 | #else | |
145 | #define AFS_DIRENT32BASESIZE IRIX5_DIRENTBASESIZE | |
146 | #define AFS_DIRENT64BASESIZE DIRENTBASESIZE | |
147 | #endif /* AFS_SGI62_ENV */ | |
148 | #else | |
149 | struct min_direct { /* miniature direct structure */ | |
150 | /* If struct direct changes, this must too */ | |
151 | #if defined(AFS_DARWIN80_ENV) | |
152 | ino_t d_fileno; | |
153 | u_short d_reclen; | |
154 | u_char d_type; | |
155 | u_char d_namlen; | |
156 | #elif defined(AFS_NBSD40_ENV) | |
157 | ino_t d_fileno; /* file number of entry */ | |
158 | uint16_t d_reclen; /* length of this record */ | |
159 | uint16_t d_namlen; /* length of string in d_name */ | |
160 | uint8_t d_type; /* file type, see below */ | |
161 | #elif defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV) | |
162 | afs_uint32 d_fileno; | |
163 | u_short d_reclen; | |
164 | u_char d_type; | |
165 | u_char d_namlen; | |
166 | #elif defined(AFS_SUN5_ENV) | |
167 | afs_uint32 d_fileno; | |
168 | afs_int32 d_off; | |
169 | u_short d_reclen; | |
170 | #else | |
171 | #if defined(AFS_AIX32_ENV) | |
172 | afs_int32 d_off; | |
173 | #elif defined(AFS_HPUX100_ENV) | |
174 | unsigned long long d_off; | |
175 | #endif | |
176 | afs_uint32 d_fileno; | |
177 | u_short d_reclen; | |
178 | u_short d_namlen; | |
179 | #endif | |
180 | }; | |
181 | #endif /* AFS_SGI_ENV */ | |
182 | ||
183 | #if defined(AFS_HPUX_ENV) | |
184 | struct minnfs_direct { | |
185 | afs_int32 d_off; /* XXX */ | |
186 | afs_uint32 d_fileno; | |
187 | u_short d_reclen; | |
188 | u_short d_namlen; | |
189 | }; | |
190 | #define NDIRSIZ_LEN(len) ((sizeof (struct dirent)+4 - (MAXNAMLEN+1)) + (((len)+1 + DIRPAD) &~ DIRPAD)) | |
191 | #endif | |
192 | #endif /* !defined(UKERNEL) */ | |
193 | ||
194 | ||
195 | /* | |
196 | *------------------------------------------------------------------------------ | |
197 | * | |
198 | * Keep a stack of about 256 fids for the bulk stat call. | |
199 | * Fill it during the readdir_move. Later empty it... | |
200 | */ | |
201 | ||
202 | #define READDIR_STASH AFSCBMAX | |
203 | struct AFSFid afs_readdir_stash[READDIR_STASH]; | |
204 | int afs_rd_stash_i = 0; | |
205 | ||
206 | /* | |
207 | *------------------------------------------------------------------------------ | |
208 | * | |
209 | * afs_readdir_move. | |
210 | * mainly a kind of macro... makes getting the struct direct | |
211 | * out to the user space easy... could take more parameters, | |
212 | * but now just takes what it needs. | |
213 | * | |
214 | * | |
215 | */ | |
216 | ||
217 | #if defined(AFS_HPUX100_ENV) | |
218 | #define DIRSIZ_LEN(len) \ | |
219 | ((sizeof (struct __dirent) - (_MAXNAMLEN+1)) + (((len)+1 + DIRPAD) &~ DIRPAD)) | |
220 | #else | |
221 | #if defined(AFS_SUN5_ENV) | |
222 | #define DIRSIZ_LEN(len) ((18 + (len) + 1 + 7) & ~7 ) | |
223 | #else | |
224 | #ifdef AFS_NBSD40_ENV | |
225 | #define DIRSIZ_LEN(len) \ | |
226 | ((sizeof (struct dirent) - (MAXNAMLEN+1)) + (((len)+1 + 7) & ~7)) | |
227 | #else | |
228 | #ifdef AFS_DIRENT | |
229 | #define DIRSIZ_LEN(len) \ | |
230 | ((sizeof (struct dirent) - (MAXNAMLEN+1)) + (((len)+1 + 3) &~ 3)) | |
231 | #else | |
232 | #if defined(AFS_SGI_ENV) | |
233 | #ifndef AFS_SGI53_ENV | |
234 | /* SGI 5.3 and later use 32/64 bit versions of directory size. */ | |
235 | #define DIRSIZ_LEN(len) DIRENTSIZE(len) | |
236 | #endif | |
237 | #else /* AFS_SGI_ENV */ | |
238 | #define DIRSIZ_LEN(len) \ | |
239 | ((sizeof (struct direct) - (MAXNAMLEN+1)) + (((len)+1 + 3) &~ 3)) | |
240 | #endif /* AFS_SGI_ENV */ | |
241 | #endif /* AFS_DIRENT */ | |
242 | #endif /* AFS_NBSD40_ENV */ | |
243 | #endif /* AFS_SUN5_ENV */ | |
244 | #endif /* AFS_HPUX100_ENV */ | |
245 | ||
246 | #if defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV) | |
247 | int | |
248 | afs_readdir_type(struct vcache *avc, struct DirEntry *ade) | |
249 | { | |
250 | struct VenusFid tfid; | |
251 | struct vcache *tvc; | |
252 | int vtype; | |
253 | tfid.Cell = avc->f.fid.Cell; | |
254 | tfid.Fid.Volume = avc->f.fid.Fid.Volume; | |
255 | tfid.Fid.Vnode = ntohl(ade->fid.vnode); | |
256 | tfid.Fid.Unique = ntohl(ade->fid.vunique); | |
257 | if ((avc->f.states & CForeign) == 0 && (ntohl(ade->fid.vnode) & 1)) { | |
258 | return DT_DIR; | |
259 | } | |
260 | ObtainReadLock(&afs_xvcache); | |
261 | if ((tvc = afs_FindVCache(&tfid, 0, 0))) { | |
262 | ReleaseReadLock(&afs_xvcache); | |
263 | if (tvc->mvstat != AFS_MVSTAT_FILE) { | |
264 | afs_PutVCache(tvc); | |
265 | return DT_DIR; | |
266 | } else if (((tvc->f.states) & (CStatd | CTruth))) { | |
267 | /* CTruth will be set if the object has | |
268 | *ever* been statd */ | |
269 | vtype = vType(tvc); | |
270 | afs_PutVCache(tvc); | |
271 | if (vtype == VDIR) | |
272 | return DT_DIR; | |
273 | else if (vtype == VREG) | |
274 | return DT_REG; | |
275 | /* Don't do this until we're sure it can't be a mtpt */ | |
276 | /* if we're CStatd and CTruth and mvstat==AFS_MVSTAT_FILE, it's a link */ | |
277 | else if (vtype == VLNK) | |
278 | return DT_LNK; | |
279 | /* what other types does AFS support? */ | |
280 | } else | |
281 | afs_PutVCache(tvc); | |
282 | } else | |
283 | ReleaseReadLock(&afs_xvcache); | |
284 | return DT_UNKNOWN; | |
285 | } | |
286 | #endif | |
287 | ||
288 | #ifdef AFS_AIX41_ENV | |
289 | #define AFS_MOVE_LOCK() AFS_GLOCK() | |
290 | #define AFS_MOVE_UNLOCK() AFS_GUNLOCK() | |
291 | #else | |
292 | #define AFS_MOVE_LOCK() | |
293 | #define AFS_MOVE_UNLOCK() | |
294 | #endif | |
295 | char bufofzeros[64]; /* gotta fill with something */ | |
296 | ||
297 | #ifdef AFS_SGI65_ENV | |
298 | int | |
299 | afs_readdir_move(struct DirEntry *de, struct vcache *vc, struct uio *auio, | |
300 | int slen, ssize_t rlen, afs_size_t off) | |
301 | #else | |
302 | int | |
303 | afs_readdir_move(struct DirEntry *de, struct vcache *vc, struct uio *auio, | |
304 | int slen, int rlen, afs_size_t off) | |
305 | #endif | |
306 | { | |
307 | int code = 0; | |
308 | struct volume *tvp; | |
309 | afs_uint32 Volume = vc->f.fid.Fid.Volume; | |
310 | afs_uint32 Vnode = de->fid.vnode; | |
311 | #if defined(AFS_SUN5_ENV) | |
312 | struct dirent64 *direntp; | |
313 | #else | |
314 | #if (defined(AFS_AIX51_ENV) && defined(AFS_64BIT_KERNEL)) | |
315 | struct dirent *direntp; | |
316 | #endif | |
317 | #endif /* AFS_SUN5_ENV */ | |
318 | #ifndef AFS_SGI53_ENV | |
319 | struct min_direct sdirEntry; | |
320 | #endif /* AFS_SGI53_ENV */ | |
321 | ||
322 | AFS_STATCNT(afs_readdir_move); | |
323 | ||
324 | #define READDIR_CORRECT_INUMS | |
325 | #ifdef READDIR_CORRECT_INUMS | |
326 | if (de->name[0] == '.' && !de->name[1]) { | |
327 | /* This is the '.' entry; if we are a volume root, we need to | |
328 | * ignore the directory and use the inum for the mount point. | |
329 | */ | |
330 | if (!FidCmp(&afs_rootFid, &vc->f.fid)) { | |
331 | Volume = 0; | |
332 | Vnode = 2; | |
333 | } else if (vc->mvstat == AFS_MVSTAT_ROOT) { | |
334 | tvp = afs_GetVolume(&vc->f.fid, 0, READ_LOCK); | |
335 | if (tvp) { | |
336 | Volume = tvp->mtpoint.Fid.Volume; | |
337 | Vnode = tvp->mtpoint.Fid.Vnode; | |
338 | afs_PutVolume(tvp, READ_LOCK); | |
339 | } | |
340 | } | |
341 | } | |
342 | else if (de->name[0] == '.' && de->name[1] == '.' && !de->name[2]) { | |
343 | /* This is the '..' entry. Getting this right is very tricky, | |
344 | * because we might be a volume root (so our parent is in a | |
345 | * different volume), or our parent might be a volume root | |
346 | * (so we actually want the mount point) or BOTH! */ | |
347 | if (!FidCmp(&afs_rootFid, &vc->f.fid)) { | |
348 | /* We are the root of the AFS root, and thus our own parent */ | |
349 | Volume = 0; | |
350 | Vnode = 2; | |
351 | } else if (vc->mvstat == AFS_MVSTAT_ROOT) { | |
352 | /* We are a volume root, which means our parent is in another | |
353 | * volume. Luckily, we should have his fid cached... */ | |
354 | if (vc->mvid.parent) { | |
355 | if (!FidCmp(&afs_rootFid, vc->mvid.parent)) { | |
356 | /* Parent directory is the root of the AFS root */ | |
357 | Volume = 0; | |
358 | Vnode = 2; | |
359 | } else if (vc->mvid.parent->Fid.Vnode == 1 | |
360 | && vc->mvid.parent->Fid.Unique == 1) { | |
361 | /* XXX The above test is evil and probably breaks DFS */ | |
362 | /* Parent directory is the target of a mount point */ | |
363 | tvp = afs_GetVolume(vc->mvid.parent, 0, READ_LOCK); | |
364 | if (tvp) { | |
365 | Volume = tvp->mtpoint.Fid.Volume; | |
366 | Vnode = tvp->mtpoint.Fid.Vnode; | |
367 | afs_PutVolume(tvp, READ_LOCK); | |
368 | } | |
369 | } else { | |
370 | /* Parent directory is not a volume root */ | |
371 | Volume = vc->mvid.parent->Fid.Volume; | |
372 | Vnode = vc->mvid.parent->Fid.Vnode; | |
373 | } | |
374 | } | |
375 | } else if (de->fid.vnode == 1 && de->fid.vunique == 1) { | |
376 | /* XXX The above test is evil and probably breaks DFS */ | |
377 | /* Parent directory is a volume root; use the right inum */ | |
378 | tvp = afs_GetVolume(&vc->f.fid, 0, READ_LOCK); | |
379 | if (tvp) { | |
380 | if (tvp->cell == afs_rootFid.Cell | |
381 | && tvp->volume == afs_rootFid.Fid.Volume) { | |
382 | /* Parent directory is the root of the AFS root */ | |
383 | Volume = 0; | |
384 | Vnode = 2; | |
385 | } else { | |
386 | /* Parent directory is the target of a mount point */ | |
387 | Volume = tvp->mtpoint.Fid.Volume; | |
388 | Vnode = tvp->mtpoint.Fid.Vnode; | |
389 | } | |
390 | afs_PutVolume(tvp, READ_LOCK); | |
391 | } | |
392 | } | |
393 | } | |
394 | #endif | |
395 | ||
396 | #ifdef AFS_SGI53_ENV | |
397 | { | |
398 | afs_int32 use64BitDirent; | |
399 | ||
400 | #ifdef AFS_SGI61_ENV | |
401 | #ifdef AFS_SGI62_ENV | |
402 | use64BitDirent = | |
403 | ABI_IS(ABI_IRIX5_64, GETDENTS_ABI(OSI_GET_CURRENT_ABI(), auio)); | |
404 | #else | |
405 | use64BitDirent = | |
406 | (auio->uio_segflg != | |
407 | UIO_USERSPACE) ? ABI_IRIX5_64 : (ABI_IS(ABI_IRIX5_64 | | |
408 | ABI_IRIX5_N32, | |
409 | u.u_procp->p_abi)); | |
410 | #endif | |
411 | #else /* AFS_SGI61_ENV */ | |
412 | use64BitDirent = | |
413 | (auio->uio_segflg != | |
414 | UIO_USERSPACE) ? ABI_IRIX5_64 : (ABI_IS(ABI_IRIX5_64, | |
415 | u.u_procp->p_abi)); | |
416 | #endif /* AFS_SGI61_ENV */ | |
417 | ||
418 | if (use64BitDirent) { | |
419 | struct min_dirent sdirEntry; | |
420 | sdirEntry.d_fileno = afs_calc_inum(vc->f.fid.Cell, | |
421 | Volume, ntohl(Vnode)); | |
422 | sdirEntry.d_reclen = rlen; | |
423 | sdirEntry.d_off = (off_t) off; | |
424 | AFS_UIOMOVE(&sdirEntry, AFS_DIRENT64BASESIZE, UIO_READ, auio, | |
425 | code); | |
426 | if (code == 0) | |
427 | AFS_UIOMOVE(de->name, slen - 1, UIO_READ, auio, code); | |
428 | if (code == 0) | |
429 | AFS_UIOMOVE(bufofzeros, | |
430 | DIRENTSIZE(slen) - (AFS_DIRENT64BASESIZE + slen - | |
431 | 1), UIO_READ, auio, code); | |
432 | if (DIRENTSIZE(slen) < rlen) { | |
433 | while (DIRENTSIZE(slen) < rlen) { | |
434 | int minLen = rlen - DIRENTSIZE(slen); | |
435 | if (minLen > sizeof(bufofzeros)) | |
436 | minLen = sizeof(bufofzeros); | |
437 | AFS_UIOMOVE(bufofzeros, minLen, UIO_READ, auio, code); | |
438 | rlen -= minLen; | |
439 | } | |
440 | } | |
441 | } else { | |
442 | struct irix5_min_dirent sdirEntry; | |
443 | sdirEntry.d_fileno = afs_calc_inum(vc->f.fid.Cell, | |
444 | Volume, ntohl(Vnode)); | |
445 | sdirEntry.d_reclen = rlen; | |
446 | sdirEntry.d_off = (afs_int32) off; | |
447 | AFS_UIOMOVE(&sdirEntry, AFS_DIRENT32BASESIZE, UIO_READ, auio, | |
448 | code); | |
449 | if (code == 0) | |
450 | AFS_UIOMOVE(de->name, slen - 1, UIO_READ, auio, code); | |
451 | if (code == 0) | |
452 | AFS_UIOMOVE(bufofzeros, | |
453 | IRIX5_DIRENTSIZE(slen) - (AFS_DIRENT32BASESIZE + | |
454 | slen - 1), UIO_READ, | |
455 | auio, code); | |
456 | if (IRIX5_DIRENTSIZE(slen) < rlen) { | |
457 | while (IRIX5_DIRENTSIZE(slen) < rlen) { | |
458 | int minLen = rlen - IRIX5_DIRENTSIZE(slen); | |
459 | if (minLen > sizeof(bufofzeros)) | |
460 | minLen = sizeof(bufofzeros); | |
461 | AFS_UIOMOVE(bufofzeros, minLen, UIO_READ, auio, code); | |
462 | rlen -= minLen; | |
463 | } | |
464 | } | |
465 | } | |
466 | } | |
467 | #else /* AFS_SGI53_ENV */ | |
468 | #if defined(AFS_SUN5_ENV) || (defined(AFS_AIX51_ENV) && defined(AFS_64BIT_KERNEL)) | |
469 | direntp = osi_AllocLargeSpace(AFS_LRALLOCSIZ); | |
470 | direntp->d_ino = afs_calc_inum(vc->f.fid.Cell, Volume, ntohl(Vnode)); | |
471 | #if defined(AFS_AIX51_ENV) && defined(AFS_64BIT_KERNEL) | |
472 | direntp->d_offset = off; | |
473 | direntp->d_namlen = slen; | |
474 | #else | |
475 | direntp->d_off = off; | |
476 | #endif | |
477 | direntp->d_reclen = rlen; | |
478 | strcpy(direntp->d_name, de->name); | |
479 | AFS_UIOMOVE((caddr_t) direntp, rlen, UIO_READ, auio, code); | |
480 | osi_FreeLargeSpace((char *)direntp); | |
481 | #else /* AFS_SUN5_ENV */ | |
482 | /* Note the odd mechanism for building the inode number */ | |
483 | sdirEntry.d_fileno = afs_calc_inum(vc->f.fid.Cell, Volume, ntohl(Vnode)); | |
484 | sdirEntry.d_reclen = rlen; | |
485 | #if !defined(AFS_SGI_ENV) | |
486 | sdirEntry.d_namlen = slen; | |
487 | #endif | |
488 | #if defined(AFS_AIX32_ENV) || defined(AFS_SGI_ENV) | |
489 | sdirEntry.d_off = off; | |
490 | #endif | |
491 | #if defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV) | |
492 | sdirEntry.d_type = afs_readdir_type(vc, de); | |
493 | #endif | |
494 | ||
495 | #if defined(AFS_SGI_ENV) | |
496 | AFS_UIOMOVE(&sdirEntry, DIRENTBASESIZE, UIO_READ, auio, code); | |
497 | if (code == 0) | |
498 | AFS_UIOMOVE(de->name, slen - 1, UIO_READ, auio, code); | |
499 | if (code == 0) | |
500 | AFS_UIOMOVE(bufofzeros, | |
501 | DIRSIZ_LEN(slen) - (DIRENTBASESIZE + slen - 1), UIO_READ, | |
502 | auio, code); | |
503 | #else /* AFS_SGI_ENV */ | |
504 | AFS_MOVE_UNLOCK(); | |
505 | #if defined(AFS_NBSD40_ENV) | |
506 | { | |
507 | struct dirent *dp; | |
508 | dp = osi_AllocLargeSpace(sizeof(struct dirent)); | |
509 | memset(dp, 0, sizeof(struct dirent)); | |
510 | dp->d_ino = afs_calc_inum(vc->f.fid.Cell, Volume, ntohl(Vnode)); | |
511 | dp->d_namlen = slen; | |
512 | dp->d_type = afs_readdir_type(vc, de); | |
513 | strcpy(dp->d_name, de->name); | |
514 | dp->d_reclen = _DIRENT_SIZE(dp) /* rlen */; | |
515 | if ((afs_debug & AFSDEB_VNLAYER) != 0) { | |
516 | afs_warn("%s: %s type %d slen %d rlen %d act. rlen %zu\n", __func__, | |
517 | dp->d_name, dp->d_type, slen, rlen, _DIRENT_SIZE(dp)); | |
518 | } | |
519 | AFS_UIOMOVE(dp, dp->d_reclen, UIO_READ, auio, code); | |
520 | osi_FreeLargeSpace((char *)dp); | |
521 | } | |
522 | #else | |
523 | AFS_UIOMOVE((char *) &sdirEntry, sizeof(sdirEntry), UIO_READ, auio, code); | |
524 | if (code == 0) { | |
525 | AFS_UIOMOVE(de->name, slen, UIO_READ, auio, code); | |
526 | } | |
527 | /* pad out the remaining characters with zeros */ | |
528 | if (code == 0) { | |
529 | AFS_UIOMOVE(bufofzeros, ((slen + 1 + DIRPAD) & ~DIRPAD) - slen, | |
530 | UIO_READ, auio, code); | |
531 | } | |
532 | #endif | |
533 | AFS_MOVE_LOCK(); | |
534 | #endif /* AFS_SGI_ENV */ | |
535 | #if !defined(AFS_NBSD_ENV) | |
536 | /* pad out the difference between rlen and slen... */ | |
537 | if (DIRSIZ_LEN(slen) < rlen) { | |
538 | AFS_MOVE_UNLOCK(); | |
539 | while (DIRSIZ_LEN(slen) < rlen) { | |
540 | int minLen = rlen - DIRSIZ_LEN(slen); | |
541 | if (minLen > sizeof(bufofzeros)) | |
542 | minLen = sizeof(bufofzeros); | |
543 | AFS_UIOMOVE(bufofzeros, minLen, UIO_READ, auio, code); | |
544 | rlen -= minLen; | |
545 | } | |
546 | AFS_MOVE_LOCK(); | |
547 | } | |
548 | #endif | |
549 | #endif /* AFS_SUN5_ENV */ | |
550 | #endif /* AFS_SGI53_ENV */ | |
551 | return (code); | |
552 | } | |
553 | ||
554 | ||
555 | /* | |
556 | *------------------------------------------------------------------------------ | |
557 | * | |
558 | * Read directory entries. | |
559 | * There are some weird things to look out for here. The uio_offset | |
560 | * field is either 0 or it is the offset returned from a previous | |
561 | * readdir. It is an opaque value used by the server to find the | |
562 | * correct directory block to read. The byte count must be at least | |
563 | * vtoblksz(vp) bytes. The count field is the number of blocks to | |
564 | * read on the server. This is advisory only, the server may return | |
565 | * only one block's worth of entries. Entries may be compressed on | |
566 | * the server. | |
567 | * | |
568 | * This routine encodes knowledge of Vice dirs. | |
569 | */ | |
570 | ||
571 | void | |
572 | afs_bulkstat_send(struct vcache *avc, struct vrequest *req) | |
573 | { | |
574 | afs_rd_stash_i = 0; | |
575 | } | |
576 | ||
577 | /* | |
578 | * Here is the bad, bad, really bad news. | |
579 | * It has to do with 'offset' (seek locations). | |
580 | */ | |
581 | ||
582 | int | |
583 | #if defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV) || defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV) | |
584 | afs_readdir(OSI_VC_DECL(avc), struct uio *auio, afs_ucred_t *acred, | |
585 | int *eofp) | |
586 | #else | |
587 | #if defined(AFS_HPUX100_ENV) | |
588 | afs_readdir2(OSI_VC_DECL(avc), struct uio *auio, afs_ucred_t *acred) | |
589 | #else | |
590 | afs_readdir(OSI_VC_DECL(avc), struct uio *auio, afs_ucred_t *acred) | |
591 | #endif | |
592 | #endif | |
593 | { | |
594 | struct vrequest *treq = NULL; | |
595 | struct dcache *tdc; | |
596 | afs_size_t origOffset, tlen; | |
597 | afs_int32 len; | |
598 | int code = 0; | |
599 | struct DirBuffer oldEntry, nextEntry; | |
600 | struct DirEntry *ode = 0, *nde = 0; | |
601 | int o_slen = 0, n_slen = 0; | |
602 | afs_int32 us; | |
603 | struct afs_fakestat_state fakestate; | |
604 | #if defined(AFS_SGI53_ENV) | |
605 | afs_int32 use64BitDirent, dirsiz; | |
606 | #endif /* defined(AFS_SGI53_ENV) */ | |
607 | #ifndef AFS_HPUX_ENV | |
608 | OSI_VC_CONVERT(avc); | |
609 | #else | |
610 | /* | |
611 | * XXX All the hacks for alloced sdirEntry and inlining of afs_readdir_move instead of calling | |
612 | * it is necessary for hpux due to stack problems that seem to occur when coming thru the nfs | |
613 | * translator side XXX | |
614 | */ | |
615 | struct min_direct *sdirEntry = osi_AllocSmallSpace(sizeof(struct min_direct)); | |
616 | afs_int32 rlen; | |
617 | #endif | |
618 | ||
619 | /* opaque value is pointer into a vice dir; use bit map to decide | |
620 | * if the entries are in use. Always assumed to be valid. 0 is | |
621 | * special, means start of a new dir. Int32 inode, followed by | |
622 | * short reclen and short namelen. Namelen does not include | |
623 | * the null byte. Followed by null-terminated string. | |
624 | */ | |
625 | AFS_STATCNT(afs_readdir); | |
626 | ||
627 | memset(&oldEntry, 0, sizeof(struct DirBuffer)); | |
628 | memset(&nextEntry, 0, sizeof(struct DirBuffer)); | |
629 | ||
630 | #if defined(AFS_SGI53_ENV) | |
631 | #ifdef AFS_SGI61_ENV | |
632 | #ifdef AFS_SGI62_ENV | |
633 | use64BitDirent = | |
634 | ABI_IS(ABI_IRIX5_64, GETDENTS_ABI(OSI_GET_CURRENT_ABI(), auio)); | |
635 | #else | |
636 | use64BitDirent = | |
637 | (auio->uio_segflg != | |
638 | UIO_USERSPACE) ? ABI_IRIX5_64 : (ABI_IS(ABI_IRIX5_64 | ABI_IRIX5_N32, | |
639 | u.u_procp->p_abi)); | |
640 | #endif /* AFS_SGI62_ENV */ | |
641 | #else /* AFS_SGI61_ENV */ | |
642 | use64BitDirent = | |
643 | (auio->uio_segflg != | |
644 | UIO_USERSPACE) ? ABI_IRIX5_64 : (ABI_IS(ABI_IRIX5_64, | |
645 | u.u_procp->p_abi)); | |
646 | #endif /* AFS_SGI61_ENV */ | |
647 | #endif /* defined(AFS_SGI53_ENV) */ | |
648 | ||
649 | #if defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV) || defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV) | |
650 | /* Not really used by the callee so we ignore it for now */ | |
651 | if (eofp) | |
652 | *eofp = 0; | |
653 | #endif | |
654 | #ifndef AFS_64BIT_CLIENT | |
655 | if (AfsLargeFileUio(auio) /* file is large than 2 GB */ | |
656 | ||AfsLargeFileSize(AFS_UIO_OFFSET(auio), AFS_UIO_RESID(auio))) | |
657 | return EFBIG; | |
658 | #endif | |
659 | ||
660 | if ((code = afs_CreateReq(&treq, acred))) { | |
661 | #ifdef AFS_HPUX_ENV | |
662 | osi_FreeSmallSpace((char *)sdirEntry); | |
663 | #endif | |
664 | return code; | |
665 | } | |
666 | /* update the cache entry */ | |
667 | afs_InitFakeStat(&fakestate); | |
668 | ||
669 | AFS_DISCON_LOCK(); | |
670 | ||
671 | code = afs_EvalFakeStat(&avc, &fakestate, treq); | |
672 | if (code) | |
673 | goto done; | |
674 | tagain: | |
675 | code = afs_VerifyVCache(avc, treq); | |
676 | if (code) | |
677 | goto done; | |
678 | /* get a reference to the entire directory */ | |
679 | tdc = afs_GetDCache(avc, (afs_size_t) 0, treq, &origOffset, &tlen, 1); | |
680 | if (!tdc) { | |
681 | code = EIO; | |
682 | goto done; | |
683 | } | |
684 | ObtainReadLock(&avc->lock); | |
685 | ObtainReadLock(&tdc->lock); | |
686 | ||
687 | /* | |
688 | * Make sure that the data in the cache is current. There are two | |
689 | * cases we need to worry about: | |
690 | * 1. The cache data is being fetched by another process. | |
691 | * 2. The cache data is no longer valid | |
692 | */ | |
693 | while ((avc->f.states & CStatd) | |
694 | && (tdc->dflags & DFFetching) | |
695 | && hsame(avc->f.m.DataVersion, tdc->f.versionNo)) { | |
696 | afs_Trace4(afs_iclSetp, CM_TRACE_DCACHEWAIT, ICL_TYPE_STRING, | |
697 | __FILE__, ICL_TYPE_INT32, __LINE__, ICL_TYPE_POINTER, tdc, | |
698 | ICL_TYPE_INT32, tdc->dflags); | |
699 | ReleaseReadLock(&tdc->lock); | |
700 | ReleaseReadLock(&avc->lock); | |
701 | afs_osi_Sleep(&tdc->validPos); | |
702 | ObtainReadLock(&avc->lock); | |
703 | ObtainReadLock(&tdc->lock); | |
704 | } | |
705 | if (!(avc->f.states & CStatd) | |
706 | || !hsame(avc->f.m.DataVersion, tdc->f.versionNo)) { | |
707 | ReleaseReadLock(&tdc->lock); | |
708 | ReleaseReadLock(&avc->lock); | |
709 | afs_PutDCache(tdc); | |
710 | goto tagain; | |
711 | } | |
712 | ||
713 | /* | |
714 | * iterator for the directory reads. Takes the AFS DirEntry | |
715 | * structure and slams them into UFS direct structures. | |
716 | * uses afs_readdir_move to get the struct to the user space. | |
717 | * | |
718 | * The routine works by looking ahead one AFS directory entry. | |
719 | * That's because the AFS entry we are currenly working with | |
720 | * may not fit into the buffer the user has provided. If it | |
721 | * doesn't we have to change the size of the LAST AFS directory | |
722 | * entry, so that it will FIT perfectly into the block the | |
723 | * user has provided. | |
724 | * | |
725 | * The 'forward looking' of the code makes it a bit tough to read. | |
726 | * Remember we need to get an entry, see if it it fits, then | |
727 | * set it up as the LAST entry, and find the next one. | |
728 | * | |
729 | * Tough to take: We give out an EINVAL if we don't have enough | |
730 | * space in the buffer, and at the same time, don't have an entry | |
731 | * to put into the buffer. This CAN happen if the first AFS entry | |
732 | * we get can't fit into the 512 character buffer provided. Seems | |
733 | * it ought not happen... | |
734 | * | |
735 | * Assumption: don't need to use anything but one dc entry: | |
736 | * this means the directory ought not be greater than 64k. | |
737 | */ | |
738 | len = 0; | |
739 | #ifdef AFS_HPUX_ENV | |
740 | auio->uio_fpflags = 0; | |
741 | #endif | |
742 | while (code == 0) { | |
743 | origOffset = AFS_UIO_OFFSET(auio); | |
744 | /* scan for the next interesting entry scan for in-use blob otherwise up point at | |
745 | * this blob note that ode, if non-zero, also represents a held dir page */ | |
746 | code = BlobScan(tdc, (origOffset >> 5), &us); | |
747 | ||
748 | if (code == 0 && us) | |
749 | code = afs_dir_GetVerifiedBlob(tdc, us, &nextEntry); | |
750 | ||
751 | if (us == 0 || code != 0) { | |
752 | code = 0; /* Reset code - keep old failure behaviour */ | |
753 | /* failed to setup nde, return what we've got, and release ode */ | |
754 | if (len) { | |
755 | /* something to hand over. */ | |
756 | #ifdef AFS_HPUX_ENV | |
757 | sdirEntry->d_fileno = afs_calc_inum(avc->f.fid.Cell, | |
758 | avc->f.fid.Fid.Volume, | |
759 | ntohl(ode->fid.vnode)); | |
760 | sdirEntry->d_reclen = rlen = AFS_UIO_RESID(auio); | |
761 | sdirEntry->d_namlen = o_slen; | |
762 | #if defined(AFS_SUN5_ENV) || defined(AFS_AIX32_ENV) || defined(AFS_HPUX100_ENV) | |
763 | sdirEntry->d_off = origOffset; | |
764 | #endif | |
765 | AFS_UIOMOVE((char *)sdirEntry, sizeof(*sdirEntry), UIO_READ, | |
766 | auio, code); | |
767 | if (code == 0) | |
768 | AFS_UIOMOVE(ode->name, o_slen, UIO_READ, auio, code); | |
769 | /* pad out the remaining characters with zeros */ | |
770 | if (code == 0) { | |
771 | AFS_UIOMOVE(bufofzeros, | |
772 | ((o_slen + 1 + DIRPAD) & ~DIRPAD) - o_slen, | |
773 | UIO_READ, auio, code); | |
774 | } | |
775 | /* pad out the difference between rlen and slen... */ | |
776 | if (DIRSIZ_LEN(o_slen) < rlen) { | |
777 | while (DIRSIZ_LEN(o_slen) < rlen) { | |
778 | int minLen = rlen - DIRSIZ_LEN(o_slen); | |
779 | if (minLen > sizeof(bufofzeros)) | |
780 | minLen = sizeof(bufofzeros); | |
781 | AFS_UIOMOVE(bufofzeros, minLen, UIO_READ, auio, code); | |
782 | rlen -= minLen; | |
783 | } | |
784 | } | |
785 | #else | |
786 | code = afs_readdir_move(ode, avc, auio, o_slen, | |
787 | #if defined(AFS_SUN5_ENV) || defined(AFS_NBSD_ENV) | |
788 | len, origOffset); | |
789 | #else | |
790 | AFS_UIO_RESID(auio), origOffset); | |
791 | #endif | |
792 | #endif /* AFS_HPUX_ENV */ | |
793 | #if !defined(AFS_SUN5_ENV) && !defined(AFS_NBSD_ENV) | |
794 | AFS_UIO_SETRESID(auio, 0); | |
795 | #endif | |
796 | } else { | |
797 | /* nothin to hand over */ | |
798 | } | |
799 | #if defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV) || defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV) | |
800 | if (eofp) | |
801 | *eofp = 1; /* Set it properly */ | |
802 | #endif | |
803 | DRelease(&oldEntry, 0); | |
804 | goto dirend; | |
805 | } | |
806 | nde = (struct DirEntry *)nextEntry.data; | |
807 | ||
808 | /* Do we have enough user space to carry out our mission? */ | |
809 | #if defined(AFS_SGI_ENV) | |
810 | n_slen = strlen(nde->name) + 1; /* NULL terminate */ | |
811 | #else | |
812 | n_slen = strlen(nde->name); | |
813 | #endif | |
814 | #ifdef AFS_SGI53_ENV | |
815 | dirsiz = | |
816 | use64BitDirent ? DIRENTSIZE(n_slen) : IRIX5_DIRENTSIZE(n_slen); | |
817 | if (dirsiz >= (AFS_UIO_RESID(auio) - len)) { | |
818 | #else | |
819 | if (DIRSIZ_LEN(n_slen) >= (AFS_UIO_RESID(auio) - len)) { | |
820 | #endif /* AFS_SGI53_ENV */ | |
821 | /* No can do no more now; ya know... at this time */ | |
822 | DRelease(&nextEntry, 0); /* can't use this one. */ | |
823 | if (len) { | |
824 | #ifdef AFS_HPUX_ENV | |
825 | sdirEntry->d_fileno = afs_calc_inum(avc->f.fid.Cell, | |
826 | avc->f.fid.Fid.Volume, | |
827 | ntohl(ode->fid.vnode)); | |
828 | sdirEntry->d_reclen = rlen = AFS_UIO_RESID(auio); | |
829 | sdirEntry->d_namlen = o_slen; | |
830 | #if defined(AFS_SUN5_ENV) || defined(AFS_AIX32_ENV) || defined(AFS_HPUX100_ENV) | |
831 | sdirEntry->d_off = origOffset; | |
832 | #endif | |
833 | AFS_UIOMOVE((char *)sdirEntry, sizeof(*sdirEntry), UIO_READ, | |
834 | auio, code); | |
835 | if (code == 0) | |
836 | AFS_UIOMOVE(ode->name, o_slen, UIO_READ, auio, code); | |
837 | /* pad out the remaining characters with zeros */ | |
838 | if (code == 0) { | |
839 | AFS_UIOMOVE(bufofzeros, | |
840 | ((o_slen + 1 + DIRPAD) & ~DIRPAD) - o_slen, | |
841 | UIO_READ, auio, code); | |
842 | } | |
843 | /* pad out the difference between rlen and slen... */ | |
844 | if (DIRSIZ_LEN(o_slen) < rlen) { | |
845 | while (DIRSIZ_LEN(o_slen) < rlen) { | |
846 | int minLen = rlen - DIRSIZ_LEN(o_slen); | |
847 | if (minLen > sizeof(bufofzeros)) | |
848 | minLen = sizeof(bufofzeros); | |
849 | AFS_UIOMOVE(bufofzeros, minLen, UIO_READ, auio, code); | |
850 | rlen -= minLen; | |
851 | } | |
852 | } | |
853 | #else /* AFS_HPUX_ENV */ | |
854 | code = | |
855 | afs_readdir_move(ode, avc, auio, o_slen, | |
856 | AFS_UIO_RESID(auio), origOffset); | |
857 | #endif /* AFS_HPUX_ENV */ | |
858 | /* this next line used to be AFSVFS40 or AIX 3.1, but is | |
859 | * really generic */ | |
860 | AFS_UIO_SETOFFSET(auio, origOffset); | |
861 | #if !defined(AFS_NBSD_ENV) | |
862 | AFS_UIO_SETRESID(auio, 0); | |
863 | #endif | |
864 | } else { /* trouble, can't give anything to the user! */ | |
865 | /* even though he has given us a buffer, | |
866 | * even though we have something to give us, | |
867 | * Looks like we lost something somewhere. | |
868 | */ | |
869 | code = EINVAL; | |
870 | } | |
871 | DRelease(&oldEntry, 0); | |
872 | goto dirend; | |
873 | } | |
874 | ||
875 | /* | |
876 | * In any event, we move out the LAST de entry, getting ready | |
877 | * to set up for the next one. | |
878 | */ | |
879 | if (len) { | |
880 | #ifdef AFS_HPUX_ENV | |
881 | sdirEntry->d_fileno = afs_calc_inum(avc->f.fid.Cell, | |
882 | avc->f.fid.Fid.Volume, | |
883 | ntohl(ode->fid.vnode)); | |
884 | sdirEntry->d_reclen = rlen = len; | |
885 | sdirEntry->d_namlen = o_slen; | |
886 | #if defined(AFS_SUN5_ENV) || defined(AFS_AIX32_ENV) || defined(AFS_HPUX100_ENV) | |
887 | sdirEntry->d_off = origOffset; | |
888 | #endif | |
889 | AFS_UIOMOVE((char *)sdirEntry, sizeof(*sdirEntry), UIO_READ, auio, | |
890 | code); | |
891 | if (code == 0) | |
892 | AFS_UIOMOVE(ode->name, o_slen, UIO_READ, auio, code); | |
893 | /* pad out the remaining characters with zeros */ | |
894 | if (code == 0) { | |
895 | AFS_UIOMOVE(bufofzeros, | |
896 | ((o_slen + 1 + DIRPAD) & ~DIRPAD) - o_slen, | |
897 | UIO_READ, auio, code); | |
898 | } | |
899 | /* pad out the difference between rlen and slen... */ | |
900 | if (DIRSIZ_LEN(o_slen) < rlen) { | |
901 | while (DIRSIZ_LEN(o_slen) < rlen) { | |
902 | int minLen = rlen - DIRSIZ_LEN(o_slen); | |
903 | if (minLen > sizeof(bufofzeros)) | |
904 | minLen = sizeof(bufofzeros); | |
905 | AFS_UIOMOVE(bufofzeros, minLen, UIO_READ, auio, code); | |
906 | rlen -= minLen; | |
907 | } | |
908 | } | |
909 | #else /* AFS_HPUX_ENV */ | |
910 | code = afs_readdir_move(ode, avc, auio, o_slen, len, origOffset); | |
911 | #endif /* AFS_HPUX_ENV */ | |
912 | } | |
913 | #ifdef AFS_SGI53_ENV | |
914 | len = use64BitDirent ? DIRENTSIZE(o_slen = | |
915 | n_slen) : IRIX5_DIRENTSIZE(o_slen = | |
916 | n_slen); | |
917 | #else | |
918 | len = DIRSIZ_LEN(o_slen = n_slen); | |
919 | #endif /* AFS_SGI53_ENV */ | |
920 | ||
921 | DRelease(&oldEntry, 0); | |
922 | oldEntry = nextEntry; | |
923 | ode = nde; | |
924 | AFS_UIO_SETOFFSET(auio, (us + afs_dir_NameBlobs(nde->name)) << 5); | |
925 | } | |
926 | ||
927 | DRelease(&oldEntry, 0); | |
928 | ||
929 | dirend: | |
930 | ReleaseReadLock(&tdc->lock); | |
931 | afs_PutDCache(tdc); | |
932 | ReleaseReadLock(&avc->lock); | |
933 | ||
934 | done: | |
935 | #ifdef AFS_HPUX_ENV | |
936 | osi_FreeSmallSpace((char *)sdirEntry); | |
937 | #endif | |
938 | AFS_DISCON_UNLOCK(); | |
939 | afs_PutFakeStat(&fakestate); | |
940 | code = afs_CheckCode(code, treq, 28); | |
941 | afs_DestroyReq(treq); | |
942 | return code; | |
943 | } | |
944 | ||
945 | #endif /* !AFS_LINUX20_ENV */ |