if (!dirpos)
break;
- de = afs_dir_GetBlob(tdc, dirpos);
- if (!de)
- break;
-
- ino = afs_calc_inum (avc->f.fid.Fid.Volume, ntohl(de->fid.vnode));
-
- if (de->name)
- len = strlen(de->name);
- else {
- printf("afs_linux_readdir: afs_dir_GetBlob failed, null name (inode %lx, dirpos %d)\n",
- (unsigned long)&tdc->f.inode, dirpos);
- DRelease(de, 0);
+ code = afs_dir_GetVerifiedBlob(tdc, dirpos, &de);
+ if (code) {
+ afs_warn("Corrupt directory (inode %lx, dirpos %d)",
+ (unsigned long)&tdc->f.inode, dirpos);
ReleaseSharedLock(&avc->lock);
afs_PutDCache(tdc);
code = -ENOENT;
goto out;
- }
+ }
+
+ ino = afs_calc_inum (avc->f.fid.Fid.Volume, ntohl(de->fid.vnode));
+ len = strlen(de->name);
/* filldir returns -EINVAL when the buffer is full. */
{
origOffset = AFS_UIO_OFFSET(auio);
/* scan for the next interesting entry scan for in-use blob otherwise up point at
* this blob note that ode, if non-zero, also represents a held dir page */
- if (!(us = BlobScan(tdc, (origOffset >> 5)))
- || !(nde = (struct DirEntry *)afs_dir_GetBlob(tdc, us))) {
+ us = BlobScan(tdc, (origOffset >> 5));
+
+ if (us)
+ afs_dir_GetVerifiedBlob(tdc, us, &nde);
+
+ if (us == 0 || nde == NULL) {
/* failed to setup nde, return what we've got, and release ode */
if (len) {
/* something to hand over. */
/* scan for the next interesting entry scan for in-use blob
* otherwise up point at this blob note that ode, if non-zero,
* also represents a held dir page */
- if (!(us = BlobScan(tdc, (origOffset >> 5)))
- || !(nde = (struct DirEntry *)afs_dir_GetBlob(tdc, us))) {
+ us = BlobScan(tdc, (orginOffset >> 5));
+
+ if (us)
+ afs_dir_GetVerifiedBlob(tdc, us, &nde);
+
+ if (us == 0 || nde == NULL) {
/* failed to setup nde, return what we've got, and release ode */
if (len) {
/* something to hand over. */
/* generic renaming */
#define NameBlobs afs_dir_NameBlobs
#define GetBlob afs_dir_GetBlob
+#define GetVerifiedBlob afs_dir_GetVerifiedBlob
#define Create afs_dir_Create
#define Length afs_dir_Length
#define Delete afs_dir_Delete
num = ntohs(dhp->hashTable[i]);
while (num != 0) {
/* Walk down the hash table list. */
- DErrno = 0;
- ep = GetBlob(dir, num);
- if (!ep) {
- if (DErrno) {
- /* we failed, return why */
- DRelease(dhp, 0);
- return DErrno;
- }
+ code = GetVerifiedBlob(dir, num, &ep);
+ if (code)
+ goto out;
+
+ if (!ep)
break;
- }
num = ntohs(ep->next);
code = (*hookproc) (hook, ep->name, ntohl(ep->fid.vnode),
ntohl(ep->fid.vunique));
DRelease(ep, 0);
if (code)
- break;
+ goto out;
}
}
+
+out:
DRelease(dhp, 0);
return 0;
}
int num;
struct DirHeader *dhp;
struct DirEntry *ep;
+ int code;
+
dhp = (struct DirHeader *)DRead(dir, 0);
if (!dhp)
return 0;
num = ntohs(dhp->hashTable[i]);
while (num != 0) {
/* Walk down the hash table list. */
- ep = GetBlob(dir, num);
+ code = GetVerifiedBlob(dir, num, &ep);
+ if (code)
+ goto out;
+
if (!ep)
break;
+
if (strcmp(ep->name, "..") && strcmp(ep->name, ".")) {
DRelease(ep, 0);
DRelease(dhp, 0);
DRelease(ep, 0);
}
}
+
+out:
DRelease(dhp, 0);
return 0;
}
+/* Return a pointer to an entry, given its number. Also return the maximum
+ * size of the entry, which is determined by its position within the directory
+ * page.
+ */
+
+static struct DirEntry *
+GetBlobWithLimit(void *dir, afs_int32 blobno, afs_size_t *maxlen)
+{
+ char *page;
+ afs_size_t pos;
+
+ *maxlen = 0;
+
+ page = DRead(dir, blobno >> LEPP);
+ if (!page)
+ return NULL;
+
+ pos = 32 * (blobno & (EPP - 1));
+
+ *maxlen = AFS_PAGESIZE - pos - 1;
+
+ return (struct DirEntry *)(page + pos);
+}
+
+/* Given an entries number, return a pointer to that entry */
struct DirEntry *
GetBlob(void *dir, afs_int32 blobno)
{
- /* Return a pointer to an entry, given its number. */
- struct DirEntry *ep;
- ep = DRead(dir, blobno >> LEPP);
- if (!ep)
- return 0;
- return (struct DirEntry *)(((long)ep) + 32 * (blobno & (EPP - 1)));
+ afs_size_t maxlen = 0;
+ return GetBlobWithLimit(dir, blobno, &maxlen);
+}
+
+/* Return an entry, having verified that the name held within the entry
+ * doesn't overflow off the end of the directory page it is contained
+ * within
+ */
+
+int
+GetVerifiedBlob(void *file, afs_int32 blobno, struct DirEntry **outdir)
+{
+ struct DirEntry *dir;
+ afs_size_t maxlen;
+ char *cp;
+
+ *outdir = NULL;
+
+ DErrno = 0;
+ dir = GetBlobWithLimit(file, blobno, &maxlen);
+ if (!dir)
+ return DErrno;
+
+ /* A blob is only valid if the name within it is NULL terminated before
+ * the end of the blob's containing page */
+ for (cp = dir->name; *cp != '\0' && cp < ((char *)dir) + maxlen; cp++);
+
+ if (*cp != '\0') {
+ DRelease(dir, 0);
+ return EIO;
+ }
+
+ *outdir = dir;
+ return 0;
}
int
struct DirHeader *dhp;
unsigned short *lp;
struct DirEntry *tp;
+
i = DirHash(ename);
dhp = (struct DirHeader *)DRead(dir, 0);
if (!dhp)
DRelease(dhp, 0);
return 0;
}
- tp = GetBlob(dir, (u_short) ntohs(dhp->hashTable[i]));
- if (!tp) {
+
+ GetVerifiedBlob(dir, (u_short) ntohs(dhp->hashTable[i]), &tp);
+ if (tp == NULL) {
DRelease(dhp, 0);
- return 0;
+ return NULL;
}
+
lp = &(dhp->hashTable[i]);
while (1) {
/* Look at each hash conflict entry. */
DRelease(lp, 0); /* Release all locks. */
return 0;
}
- tp = GetBlob(dir, (u_short) ntohs(tp->next));
- if (!tp) {
+ GetVerifiedBlob(dir, (u_short) ntohs(tp->next), &tp);
+ if (tp == NULL) {
DRelease(lp, 0);
- return 0;
+ return NULL;
}
}
}
struct DirHeader *dhp;
unsigned short *lp;
struct DirEntry *tp;
+
dhp = (struct DirHeader *) DRead(dir,0);
if (!dhp) return 0;
for (i=0; i<NHASHENT; i++) {
if (dhp->hashTable[i] != 0) {
- tp = GetBlob(dir,(u_short)ntohs(dhp->hashTable[i]));
+ GetVerifiedBlob(dir,(u_short)ntohs(dhp->hashTable[i]), &tp);
if (!tp) { /* should not happen */
DRelease(dhp, 0);
return 0;
lp = &(tp->next);
if (tp->next == 0)
break;
- tp = GetBlob(dir,(u_short)ntohs(tp->next));
+ GetVerifiedBlob(dir,(u_short)ntohs(tp->next), &tp);
DRelease(lp, 0);
}
DRelease(lp, 0);
void *hook);
extern int IsEmpty(void *dir);
extern struct DirEntry *GetBlob(void *dir, afs_int32 blobno);
+extern int GetVerifiedBlob(void *dir, afs_int32 blobno, struct DirEntry **);
extern int DirHash(char *string);
extern int DStat(int *abuffers, int *acalls, int *aios);
extern int afs_dir_ChangeFid(void *dir, char *entry, afs_uint32 *old_fid,
afs_uint32 *new_fid);
extern struct DirEntry *afs_dir_GetBlob(void *dir, afs_int32 blobno);
+extern int afs_dir_GetVerifiedBlob(void *, afs_int32, struct DirEntry **);
#endif
#endif /* !defined(__AFS_DIR_H) */