/*
* UNFS3 readdir routine
* (C) 2004, Pascal Schmidt
* see file LICENSE for license details
*/
#include "config.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <rpc/rpc.h>
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include "nfs.h"
#include "mount.h"
#include "fh.h"
#include "readdir.h"
#include "backend.h"
#include "Config/exports.h"
#include "error.h"
/*
* maximum number of entries in readdir results
*
* this is 4096 / 28 (the minimum size of an entry3)
*/
#define MAX_ENTRIES 143
/*
* static READDIR3resok size with XDR overhead
*
* 88 bytes attributes, 8 bytes verifier, 4 bytes value_follows for
* first entry, 4 bytes eof flag
*/
#define RESOK_SIZE 104
/*
* static entry3 size with XDR overhead
*
* 8 bytes fileid, 4 bytes name length, 8 bytes cookie, 4 byte value_follows
*/
#define ENTRY_SIZE 24
/*
* size of a name with XDR overhead
*
* XDR pads to multiple of 4 bytes
*/
#define NAME_SIZE(x) (((strlen((x))+3)/4)*4)
/*
* check if directory cookie is still valid
*/
static int cookie_check(time_t time, cookieverf3 verf)
{
return (int) (time == *(time_t *) verf);
}
uint32 directory_hash(const char *path)
{
DIR *search;
struct dirent *this;
uint32 hval = 0;
search = backend_opendir(path);
if (!search) {
return 0;
}
while ((this = backend_readdir(search)) != NULL) {
hval = fnv1a_32(this->d_name, hval);
}
backend_closedir(search);
return hval;
}
/*
* perform a READDIR operation
*
* fh_decomp must be called directly before to fill the stat cache
*/
READDIR3res read_dir(const char *path, cookie3 cookie, cookieverf3 verf,
count3 count)
{
READDIR3res result;
READDIR3resok resok;
static entry3 entry[MAX_ENTRIES];
struct stat buf;
int res;
DIR *search;
struct dirent *this;
count3 i, real_count;
static char obj[NFS_MAXPATHLEN * MAX_ENTRIES];
char scratch[NFS_MAXPATHLEN];
/* we refuse to return more than 4k from READDIR */
if (count > 4096)
count = 4096;
/* account for size of information heading resok structure */
real_count = RESOK_SIZE;
/* check verifier against directory's modification time */
if (cookie != 0 && !cookie_check(st_cache.st_mtime, verf)) {
result.status = NFS3ERR_BAD_COOKIE;
return result;
}
/* compute new cookie verifier */
memset(verf, 0, NFS3_COOKIEVERFSIZE);
*(time_t *) verf = st_cache.st_mtime;
search = backend_opendir(path);
if (!search) {
if ((exports_opts & OPT_REMOVABLE) && (export_point(path))) {
/* Removable media export point; probably no media inserted.
Return empty directory. */
memset(resok.cookieverf, 0, NFS3_COOKIEVERFSIZE);
resok.reply.entries = NULL;
resok.reply.eof = TRUE;
result.status = NFS3_OK;
result.READDIR3res_u.resok = resok;
return result;
} else {
result.status = readdir_err();
return result;
}
}
this = backend_readdir(search);
for (i = 0; i < cookie; i++)
if (this)
this = backend_readdir(search);
i = 0;
entry[0].name = NULL;
while (this && real_count < count && i < MAX_ENTRIES) {
if (i > 0)
entry[i - 1].nextentry = &entry[i];
if (strlen(path) + strlen(this->d_name) + 1 < NFS_MAXPATHLEN) {
if (strcmp(path, "/") == 0)
sprintf(scratch, "/%s", this->d_name);
else
sprintf(scratch, "%s/%s", path, this->d_name);
res = backend_lstat(scratch, &buf);
if (res == -1) {
result.status = readdir_err();
backend_closedir(search);
return result;
}
strcpy(&obj[i * NFS_MAXPATHLEN], this->d_name);
entry[i].fileid = ((uint64) buf.st_dev << 32)
+ buf.st_ino;
entry[i].name = &obj[i * NFS_MAXPATHLEN];
entry[i].cookie = cookie + 1 + i;
entry[i].nextentry = NULL;
/* account for entry size */
real_count += ENTRY_SIZE + NAME_SIZE(this->d_name);
/* whoops, overflowed the maximum size */
if (real_count > count && i > 0)
entry[i - 1].nextentry = NULL;
else {
/* advance to next entry */
this = backend_readdir(search);
}
i++;
} else {
result.status = NFS3ERR_IO;
backend_closedir(search);
return result;
}
}
backend_closedir(search);
if (entry[0].name)
resok.reply.entries = &entry[0];
else
resok.reply.entries = NULL;
if (this)
resok.reply.eof = FALSE;
else
resok.reply.eof = TRUE;
memcpy(resok.cookieverf, verf, NFS3_COOKIEVERFSIZE);
result.status = NFS3_OK;
result.READDIR3res_u.resok = resok;
return result;
}
syntax highlighted by Code2HTML, v. 0.9.1