use readdir to generate ea list
when given a null for FILE_GET_EA_INFORMATION, use READDIR to construct one with the full named attribute directory listing new function read_entire_dir() allocates an initial buffer for readdir entries a nd calls nfs41_readdir() repeatedly to fill it. the buffer is realloc()ed as mo re space is required, allowing the function to return a complete listing in a si ngle buffer new function calculate_ea_list_length() calculates the exact length of the FILE_ GET_EA_INFORMATION buffer required to hold all of the names from the directory l isting once the FILE_GET_EA_INFORMATION buffer is allocated, it is filled by populate_e a_list() and cached with nfs41_open_state handle_getexattr() remains largely unchanged Signed-off-by: Casey Bodley <cbodley@citi.umich.edu>
This commit is contained in:
parent
7b8715ce8e
commit
17aac16946
3 changed files with 199 additions and 2 deletions
187
daemon/ea.c
187
daemon/ea.c
|
|
@ -108,6 +108,8 @@ static int is_cygwin_ea(
|
||||||
&& sizeof("NfsSymlinkTargetName")-1 == ea->EaNameLength);
|
&& sizeof("NfsSymlinkTargetName")-1 == ea->EaNameLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define NEXT_ENTRY(ea) ((PBYTE)(ea) + (ea)->NextEntryOffset)
|
||||||
|
|
||||||
int nfs41_ea_set(
|
int nfs41_ea_set(
|
||||||
IN nfs41_open_state *state,
|
IN nfs41_open_state *state,
|
||||||
IN PFILE_FULL_EA_INFORMATION ea)
|
IN PFILE_FULL_EA_INFORMATION ea)
|
||||||
|
|
@ -128,7 +130,7 @@ int nfs41_ea_set(
|
||||||
|
|
||||||
if (ea->NextEntryOffset == 0)
|
if (ea->NextEntryOffset == 0)
|
||||||
break;
|
break;
|
||||||
ea = (PFILE_FULL_EA_INFORMATION)((PBYTE)ea + ea->NextEntryOffset);
|
ea = (PFILE_FULL_EA_INFORMATION)NEXT_ENTRY(ea);
|
||||||
}
|
}
|
||||||
out:
|
out:
|
||||||
return status;
|
return status;
|
||||||
|
|
@ -225,6 +227,182 @@ out:
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define READDIR_LEN_INITIAL 8192
|
||||||
|
#define READDIR_LEN_MIN 2048
|
||||||
|
|
||||||
|
/* call readdir repeatedly to get a complete list of entries */
|
||||||
|
static int read_entire_dir(
|
||||||
|
IN nfs41_session *session,
|
||||||
|
IN nfs41_path_fh *eadir,
|
||||||
|
OUT unsigned char **buffer_out,
|
||||||
|
OUT uint32_t *length_out)
|
||||||
|
{
|
||||||
|
nfs41_readdir_cookie cookie = { 0 };
|
||||||
|
bitmap4 attr_request;
|
||||||
|
nfs41_readdir_entry *last_entry;
|
||||||
|
unsigned char *buffer;
|
||||||
|
uint32_t buffer_len, len, total_len;
|
||||||
|
bool_t eof;
|
||||||
|
int status = NO_ERROR;
|
||||||
|
|
||||||
|
attr_request.count = 0; /* don't request attributes */
|
||||||
|
|
||||||
|
/* allocate the buffer for readdir entries */
|
||||||
|
buffer_len = READDIR_LEN_INITIAL;
|
||||||
|
buffer = calloc(1, buffer_len);
|
||||||
|
if (buffer == NULL) {
|
||||||
|
status = GetLastError();
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
last_entry = NULL;
|
||||||
|
total_len = 0;
|
||||||
|
eof = FALSE;
|
||||||
|
|
||||||
|
while (!eof) {
|
||||||
|
len = buffer_len - total_len;
|
||||||
|
if (len < READDIR_LEN_MIN) {
|
||||||
|
const ptrdiff_t diff = (unsigned char*)last_entry - buffer;
|
||||||
|
/* realloc the buffer to fit more entries */
|
||||||
|
unsigned char *tmp = realloc(buffer, buffer_len * 2);
|
||||||
|
if (tmp == NULL) {
|
||||||
|
status = GetLastError();
|
||||||
|
goto out_free;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (last_entry) /* fix last_entry pointer */
|
||||||
|
last_entry = (nfs41_readdir_entry*)(tmp + diff);
|
||||||
|
buffer = tmp;
|
||||||
|
buffer_len *= 2;
|
||||||
|
len = buffer_len - total_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* fetch the next group of entries */
|
||||||
|
status = nfs41_readdir(session, eadir, &attr_request,
|
||||||
|
&cookie, buffer + total_len, &len, &eof);
|
||||||
|
if (status)
|
||||||
|
goto out_free;
|
||||||
|
|
||||||
|
if (last_entry == NULL) {
|
||||||
|
/* initialize last_entry to the front of the list */
|
||||||
|
last_entry = (nfs41_readdir_entry*)(buffer + total_len);
|
||||||
|
} else if (len) {
|
||||||
|
/* link the previous list to the new one */
|
||||||
|
last_entry->next_entry_offset = (uint32_t)FIELD_OFFSET(
|
||||||
|
nfs41_readdir_entry, name) + last_entry->name_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* find the new last entry */
|
||||||
|
while (last_entry->next_entry_offset) {
|
||||||
|
last_entry = (nfs41_readdir_entry*)((char*)last_entry +
|
||||||
|
last_entry->next_entry_offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
cookie.cookie = last_entry->cookie;
|
||||||
|
total_len += len;
|
||||||
|
}
|
||||||
|
|
||||||
|
*buffer_out = buffer;
|
||||||
|
*length_out = total_len;
|
||||||
|
out:
|
||||||
|
return status;
|
||||||
|
|
||||||
|
out_free:
|
||||||
|
free(buffer);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define ALIGNED_EASIZE(len) (align4(sizeof(FILE_GET_EA_INFORMATION) + len))
|
||||||
|
|
||||||
|
static uint32_t calculate_ea_list_length(
|
||||||
|
IN const unsigned char *position,
|
||||||
|
IN uint32_t remaining)
|
||||||
|
{
|
||||||
|
const nfs41_readdir_entry *entry;
|
||||||
|
uint32_t length = 0;
|
||||||
|
|
||||||
|
while (remaining) {
|
||||||
|
entry = (const nfs41_readdir_entry*)position;
|
||||||
|
length += ALIGNED_EASIZE(entry->name_len);
|
||||||
|
|
||||||
|
if (!entry->next_entry_offset)
|
||||||
|
break;
|
||||||
|
|
||||||
|
position += entry->next_entry_offset;
|
||||||
|
remaining -= entry->next_entry_offset;
|
||||||
|
}
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void populate_ea_list(
|
||||||
|
IN const unsigned char *position,
|
||||||
|
OUT PFILE_GET_EA_INFORMATION ea_list)
|
||||||
|
{
|
||||||
|
const nfs41_readdir_entry *entry;
|
||||||
|
PFILE_GET_EA_INFORMATION ea = ea_list, prev = NULL;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
entry = (const nfs41_readdir_entry*)position;
|
||||||
|
StringCchCopyA(ea->EaName, entry->name_len, entry->name);
|
||||||
|
ea->EaNameLength = (UCHAR)entry->name_len - 1;
|
||||||
|
|
||||||
|
if (!entry->next_entry_offset) {
|
||||||
|
ea->NextEntryOffset = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
prev = ea;
|
||||||
|
ea->NextEntryOffset = ALIGNED_EASIZE(ea->EaNameLength);
|
||||||
|
ea = (PFILE_GET_EA_INFORMATION)NEXT_ENTRY(ea);
|
||||||
|
position += entry->next_entry_offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int get_ea_list(
|
||||||
|
IN OUT nfs41_open_state *state,
|
||||||
|
IN nfs41_path_fh *eadir,
|
||||||
|
OUT PFILE_GET_EA_INFORMATION *ealist_out)
|
||||||
|
{
|
||||||
|
unsigned char *entry_list;
|
||||||
|
PFILE_GET_EA_INFORMATION ea_list;
|
||||||
|
uint32_t entry_len, ea_size;
|
||||||
|
int status = NO_ERROR;
|
||||||
|
|
||||||
|
EnterCriticalSection(&state->ea.lock);
|
||||||
|
|
||||||
|
if (state->ea.list != INVALID_HANDLE_VALUE) {
|
||||||
|
/* use cached ea names */
|
||||||
|
*ealist_out = state->ea.list;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* read the entire directory into a nfs41_readdir_entry buffer */
|
||||||
|
status = read_entire_dir(state->session, eadir, &entry_list, &entry_len);
|
||||||
|
if (status)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
ea_size = calculate_ea_list_length(entry_list, entry_len);
|
||||||
|
if (ea_size == 0) {
|
||||||
|
*ealist_out = state->ea.list = NULL;
|
||||||
|
goto out_free;
|
||||||
|
}
|
||||||
|
ea_list = calloc(1, ea_size);
|
||||||
|
if (ea_list == NULL) {
|
||||||
|
status = GetLastError();
|
||||||
|
goto out_free;
|
||||||
|
}
|
||||||
|
|
||||||
|
populate_ea_list(entry_list, ea_list);
|
||||||
|
|
||||||
|
*ealist_out = state->ea.list = ea_list;
|
||||||
|
|
||||||
|
out_free:
|
||||||
|
free(entry_list); /* allocated by read_entire_dir() */
|
||||||
|
out:
|
||||||
|
LeaveCriticalSection(&state->ea.lock);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
static int handle_getexattr(nfs41_upcall *upcall)
|
static int handle_getexattr(nfs41_upcall *upcall)
|
||||||
{
|
{
|
||||||
int status = 0;
|
int status = 0;
|
||||||
|
|
@ -251,6 +429,13 @@ static int handle_getexattr(nfs41_upcall *upcall)
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (gea == NULL) {
|
||||||
|
/* if no names are queried, use READDIR to list them all */
|
||||||
|
status = get_ea_list(state, &parent, &gea);
|
||||||
|
if (status)
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
entry_buf = malloc(UPCALL_BUF_SIZE);
|
entry_buf = malloc(UPCALL_BUF_SIZE);
|
||||||
if (entry_buf == NULL) {
|
if (entry_buf == NULL) {
|
||||||
status = GetLastError();
|
status = GetLastError();
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,9 @@ struct __nfs41_client;
|
||||||
struct __rpc_client;
|
struct __rpc_client;
|
||||||
struct __nfs41_root;
|
struct __nfs41_root;
|
||||||
|
|
||||||
|
struct _FILE_GET_EA_INFORMATION;
|
||||||
|
struct _FILE_FULL_EA_INFORMATION;
|
||||||
|
|
||||||
typedef struct __nfs41_superblock {
|
typedef struct __nfs41_superblock {
|
||||||
nfs41_fsid fsid;
|
nfs41_fsid fsid;
|
||||||
struct list_entry entry; /* position in nfs41_server.superblocks */
|
struct list_entry entry; /* position in nfs41_server.superblocks */
|
||||||
|
|
@ -152,6 +155,11 @@ typedef struct __nfs41_open_state {
|
||||||
CRITICAL_SECTION lock;
|
CRITICAL_SECTION lock;
|
||||||
} locks;
|
} locks;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
struct _FILE_GET_EA_INFORMATION *list;
|
||||||
|
CRITICAL_SECTION lock;
|
||||||
|
} ea;
|
||||||
|
|
||||||
HANDLE srv_open; /* for data cache invalidation */
|
HANDLE srv_open; /* for data cache invalidation */
|
||||||
} nfs41_open_state;
|
} nfs41_open_state;
|
||||||
|
|
||||||
|
|
@ -505,7 +513,6 @@ void nfs41_open_stateid_arg(
|
||||||
|
|
||||||
|
|
||||||
/* ea.c */
|
/* ea.c */
|
||||||
struct _FILE_FULL_EA_INFORMATION;
|
|
||||||
int nfs41_ea_set(
|
int nfs41_ea_set(
|
||||||
IN nfs41_open_state *state,
|
IN nfs41_open_state *state,
|
||||||
IN struct _FILE_FULL_EA_INFORMATION *ea);
|
IN struct _FILE_FULL_EA_INFORMATION *ea);
|
||||||
|
|
|
||||||
|
|
@ -63,6 +63,9 @@ static int create_open_state(
|
||||||
list_init(&state->client_entry);
|
list_init(&state->client_entry);
|
||||||
InitializeCriticalSection(&state->locks.lock);
|
InitializeCriticalSection(&state->locks.lock);
|
||||||
|
|
||||||
|
state->ea.list = INVALID_HANDLE_VALUE;
|
||||||
|
InitializeCriticalSection(&state->ea.lock);
|
||||||
|
|
||||||
*state_out = state;
|
*state_out = state;
|
||||||
status = NO_ERROR;
|
status = NO_ERROR;
|
||||||
out:
|
out:
|
||||||
|
|
@ -83,6 +86,8 @@ static void open_state_free(
|
||||||
free(list_container(entry, nfs41_lock_state, open_entry));
|
free(list_container(entry, nfs41_lock_state, open_entry));
|
||||||
if (state->delegation.state)
|
if (state->delegation.state)
|
||||||
nfs41_delegation_deref(state->delegation.state);
|
nfs41_delegation_deref(state->delegation.state);
|
||||||
|
if (state->ea.list != INVALID_HANDLE_VALUE)
|
||||||
|
free(state->ea.list);
|
||||||
free(state);
|
free(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue