mirror of
https://github.com/reactos/reactos.git
synced 2024-11-24 03:53:31 +08:00
303 lines
10 KiB
C
303 lines
10 KiB
C
/* NFSv4.1 client for Windows
|
|
* Copyright © 2012 The Regents of the University of Michigan
|
|
*
|
|
* Olga Kornievskaia <aglo@umich.edu>
|
|
* Casey Bodley <cbodley@umich.edu>
|
|
*
|
|
* This library is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU Lesser General Public License as published by
|
|
* the Free Software Foundation; either version 2.1 of the License, or (at
|
|
* your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful, but
|
|
* without any warranty; without even the implied warranty of merchantability
|
|
* or fitness for a particular purpose. See the GNU Lesser General Public
|
|
* License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
* along with this library; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
*/
|
|
|
|
#include <windows.h>
|
|
#include <stdio.h>
|
|
|
|
#include "daemon_debug.h"
|
|
#include "nfs41.h"
|
|
#include "nfs41_ops.h"
|
|
#include "from_kernel.h"
|
|
#include "util.h"
|
|
|
|
|
|
#define SBLVL 3 /* dprintf level for superblock logging */
|
|
|
|
|
|
static __inline int compare_fsid(
|
|
IN const nfs41_fsid *lhs,
|
|
IN const nfs41_fsid *rhs)
|
|
{
|
|
if (lhs->major > rhs->major) return 1;
|
|
if (lhs->major < rhs->major) return -1;
|
|
if (lhs->minor > rhs->minor) return 1;
|
|
if (lhs->minor < rhs->minor) return -1;
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* nfs41_superblock */
|
|
static int superblock_create(
|
|
IN const nfs41_fsid *fsid,
|
|
OUT nfs41_superblock **superblock_out)
|
|
{
|
|
int status = NO_ERROR;
|
|
nfs41_superblock *superblock;
|
|
|
|
dprintf(SBLVL, "creating superblock for fsid(%llu,%llu)\n",
|
|
fsid->major, fsid->minor);
|
|
|
|
superblock = calloc(1, sizeof(nfs41_superblock));
|
|
if (superblock == NULL) {
|
|
status = GetLastError();
|
|
eprintf("failed to allocate superblock "
|
|
"for fsid(%llu,%llu)\n", fsid->major, fsid->minor);
|
|
goto out;
|
|
}
|
|
|
|
memcpy(&superblock->fsid, fsid, sizeof(nfs41_fsid));
|
|
InitializeSRWLock(&superblock->lock);
|
|
|
|
*superblock_out = superblock;
|
|
out:
|
|
return status;
|
|
}
|
|
|
|
static int get_superblock_attrs(
|
|
IN nfs41_session *session,
|
|
IN nfs41_superblock *superblock,
|
|
IN nfs41_path_fh *file)
|
|
{
|
|
bool_t supports_named_attrs;
|
|
int status;
|
|
bitmap4 attr_request;
|
|
nfs41_file_info info = { 0 };
|
|
|
|
attr_request.arr[0] = FATTR4_WORD0_SUPPORTED_ATTRS |
|
|
FATTR4_WORD0_LINK_SUPPORT | FATTR4_WORD0_SYMLINK_SUPPORT |
|
|
FATTR4_WORD0_ACLSUPPORT | FATTR4_WORD0_CANSETTIME |
|
|
FATTR4_WORD0_CASE_INSENSITIVE | FATTR4_WORD0_CASE_PRESERVING |
|
|
FATTR4_WORD0_MAXREAD | (uint32_t)(FATTR4_WORD0_MAXWRITE);
|
|
attr_request.arr[1] = FATTR4_WORD1_FS_LAYOUT_TYPE |
|
|
FATTR4_WORD1_TIME_DELTA;
|
|
attr_request.arr[2] = FATTR4_WORD2_SUPPATTR_EXCLCREAT;
|
|
attr_request.count = 3;
|
|
|
|
info.supported_attrs = &superblock->supported_attrs;
|
|
info.suppattr_exclcreat = &superblock->suppattr_exclcreat;
|
|
info.time_delta = &superblock->time_delta;
|
|
|
|
status = nfs41_superblock_getattr(session, file,
|
|
&attr_request, &info, &supports_named_attrs);
|
|
if (status) {
|
|
eprintf("nfs41_superblock_getattr() failed with %s when fetching "
|
|
"attributes for fsid(%llu,%llu)\n", nfs_error_string(status),
|
|
superblock->fsid.major, superblock->fsid.minor);
|
|
goto out;
|
|
}
|
|
|
|
if (info.maxread)
|
|
superblock->maxread = info.maxread;
|
|
else
|
|
superblock->maxread = session->fore_chan_attrs.ca_maxresponsesize;
|
|
|
|
if (info.maxwrite)
|
|
superblock->maxwrite = info.maxwrite;
|
|
else
|
|
superblock->maxwrite = session->fore_chan_attrs.ca_maxrequestsize;
|
|
|
|
superblock->layout_types = info.fs_layout_types;
|
|
superblock->aclsupport = info.aclsupport;
|
|
superblock->link_support = info.link_support;
|
|
superblock->symlink_support = info.symlink_support;
|
|
superblock->ea_support = supports_named_attrs;
|
|
superblock->case_preserving = info.case_preserving;
|
|
superblock->case_insensitive = info.case_insensitive;
|
|
|
|
if (bitmap_isset(&info.attrmask, 0, FATTR4_WORD0_CANSETTIME))
|
|
superblock->cansettime = info.cansettime;
|
|
else /* cansettime is not supported, try setting them anyway */
|
|
superblock->cansettime = 1;
|
|
|
|
/* if time_delta is not supported, default to 1s */
|
|
if (!bitmap_isset(&info.attrmask, 1, FATTR4_WORD1_TIME_DELTA))
|
|
superblock->time_delta.seconds = 1;
|
|
|
|
/* initialize the default getattr mask */
|
|
superblock->default_getattr.count = 2;
|
|
superblock->default_getattr.arr[0] = FATTR4_WORD0_TYPE
|
|
| FATTR4_WORD0_CHANGE | FATTR4_WORD0_SIZE
|
|
| FATTR4_WORD0_FILEID | FATTR4_WORD0_HIDDEN
|
|
| FATTR4_WORD0_ARCHIVE;
|
|
superblock->default_getattr.arr[1] = FATTR4_WORD1_MODE
|
|
| FATTR4_WORD1_NUMLINKS | FATTR4_WORD1_SYSTEM
|
|
| FATTR4_WORD1_TIME_ACCESS | FATTR4_WORD1_TIME_CREATE
|
|
| FATTR4_WORD1_TIME_MODIFY;
|
|
superblock->default_getattr.arr[2] = 0;
|
|
|
|
nfs41_superblock_supported_attrs(superblock, &superblock->default_getattr);
|
|
|
|
dprintf(SBLVL, "attributes for fsid(%llu,%llu): "
|
|
"maxread=%llu, maxwrite=%llu, layout_types: 0x%X, "
|
|
"cansettime=%u, time_delta={%llu,%u}, aclsupport=%u, "
|
|
"link_support=%u, symlink_support=%u, case_preserving=%u, "
|
|
"case_insensitive=%u\n",
|
|
superblock->fsid.major, superblock->fsid.minor,
|
|
superblock->maxread, superblock->maxwrite,
|
|
superblock->layout_types, superblock->cansettime,
|
|
superblock->time_delta.seconds, superblock->time_delta.nseconds,
|
|
superblock->aclsupport, superblock->link_support,
|
|
superblock->symlink_support, superblock->case_preserving,
|
|
superblock->case_insensitive);
|
|
out:
|
|
return status;
|
|
}
|
|
|
|
void nfs41_superblock_fs_attributes(
|
|
IN const nfs41_superblock *superblock,
|
|
OUT PFILE_FS_ATTRIBUTE_INFORMATION FsAttrs)
|
|
{
|
|
FsAttrs->FileSystemAttributes = FILE_SUPPORTS_REMOTE_STORAGE;
|
|
if (superblock->link_support)
|
|
FsAttrs->FileSystemAttributes |= FILE_SUPPORTS_HARD_LINKS;
|
|
if (superblock->symlink_support)
|
|
FsAttrs->FileSystemAttributes |= FILE_SUPPORTS_REPARSE_POINTS;
|
|
if (superblock->ea_support)
|
|
FsAttrs->FileSystemAttributes |= FILE_SUPPORTS_EXTENDED_ATTRIBUTES;
|
|
if (superblock->case_preserving)
|
|
FsAttrs->FileSystemAttributes |= FILE_CASE_PRESERVED_NAMES;
|
|
if (!superblock->case_insensitive)
|
|
FsAttrs->FileSystemAttributes |= FILE_CASE_SENSITIVE_SEARCH;
|
|
if (superblock->aclsupport)
|
|
FsAttrs->FileSystemAttributes |= FILE_PERSISTENT_ACLS;
|
|
|
|
FsAttrs->MaximumComponentNameLength = NFS41_MAX_COMPONENT_LEN;
|
|
|
|
/* let the driver fill in FileSystemName */
|
|
FsAttrs->FileSystemNameLength = 0;
|
|
|
|
dprintf(SBLVL, "FileFsAttributeInformation: case_preserving %u, "
|
|
"case_insensitive %u, max component %u\n",
|
|
superblock->case_preserving, superblock->case_insensitive,
|
|
FsAttrs->MaximumComponentNameLength);
|
|
}
|
|
|
|
|
|
/* nfs41_superblock_list */
|
|
#define superblock_entry(pos) list_container(pos, nfs41_superblock, entry)
|
|
|
|
static int superblock_compare(
|
|
const struct list_entry *entry,
|
|
const void *value)
|
|
{
|
|
const nfs41_superblock *superblock = superblock_entry(entry);
|
|
return compare_fsid(&superblock->fsid, (const nfs41_fsid*)value);
|
|
}
|
|
|
|
static nfs41_superblock* find_superblock(
|
|
IN nfs41_superblock_list *superblocks,
|
|
IN const nfs41_fsid *fsid)
|
|
{
|
|
struct list_entry *entry;
|
|
entry = list_search(&superblocks->head, fsid, superblock_compare);
|
|
return entry ? superblock_entry(entry) : NULL;
|
|
}
|
|
|
|
void nfs41_superblock_list_init(
|
|
IN nfs41_superblock_list *superblocks)
|
|
{
|
|
list_init(&superblocks->head);
|
|
InitializeSRWLock(&superblocks->lock);
|
|
}
|
|
|
|
void nfs41_superblock_list_free(
|
|
IN nfs41_superblock_list *superblocks)
|
|
{
|
|
struct list_entry *entry, *tmp;
|
|
|
|
dprintf(SBLVL, "nfs41_superblock_list_free()\n");
|
|
|
|
list_for_each_tmp(entry, tmp, &superblocks->head)
|
|
free(superblock_entry(entry));
|
|
}
|
|
|
|
|
|
int nfs41_superblock_for_fh(
|
|
IN nfs41_session *session,
|
|
IN const nfs41_fsid *fsid,
|
|
IN const nfs41_fh *parent OPTIONAL,
|
|
OUT nfs41_path_fh *file)
|
|
{
|
|
int status = NFS4_OK;
|
|
nfs41_server *server = client_server(session->client);
|
|
nfs41_superblock_list *superblocks = &server->superblocks;
|
|
nfs41_superblock *superblock;
|
|
|
|
dprintf(SBLVL, "--> nfs41_superblock_for_fh(fsid(%llu,%llu))\n",
|
|
fsid->major, fsid->minor);
|
|
|
|
/* compare with the parent's fsid, and use that if it matches */
|
|
if (parent && parent->superblock &&
|
|
compare_fsid(fsid, &parent->superblock->fsid) == 0) {
|
|
file->fh.superblock = parent->superblock;
|
|
dprintf(SBLVL, "using superblock from parent\n");
|
|
goto out;
|
|
}
|
|
|
|
/* using a shared lock, search for an existing superblock */
|
|
AcquireSRWLockShared(&superblocks->lock);
|
|
superblock = find_superblock(superblocks, fsid);
|
|
ReleaseSRWLockShared(&superblocks->lock);
|
|
|
|
if (superblock) {
|
|
dprintf(SBLVL, "found existing superblock in server list "
|
|
"[shared lock]\n");
|
|
} else {
|
|
AcquireSRWLockExclusive(&superblocks->lock);
|
|
/* must search again under an exclusive lock, in case another thread
|
|
* created it after our first search */
|
|
superblock = find_superblock(superblocks, fsid);
|
|
if (superblock) {
|
|
dprintf(SBLVL, "found newly created superblock in server list "
|
|
"[exclusive lock]\n");
|
|
} else {
|
|
/* create the superblock */
|
|
status = superblock_create(fsid, &superblock);
|
|
if (status == NO_ERROR) /* add it to the list */
|
|
list_add_tail(&superblocks->head, &superblock->entry);
|
|
}
|
|
ReleaseSRWLockExclusive(&superblocks->lock);
|
|
}
|
|
|
|
if (status == NO_ERROR && superblock->supported_attrs.count == 0) {
|
|
/* exclusive lock on the superblock while fetching attributes */
|
|
AcquireSRWLockExclusive(&superblock->lock);
|
|
if (superblock->supported_attrs.count == 0)
|
|
status = get_superblock_attrs(session, superblock, file);
|
|
ReleaseSRWLockExclusive(&superblock->lock);
|
|
}
|
|
|
|
file->fh.superblock = superblock;
|
|
out:
|
|
dprintf(SBLVL, "<-- nfs41_superblock_for_fh() returning %p, status %d\n",
|
|
file->fh.superblock, status);
|
|
return status;
|
|
}
|
|
|
|
void nfs41_superblock_space_changed(
|
|
IN nfs41_superblock *superblock)
|
|
{
|
|
/* invalidate cached volume size attributes */
|
|
AcquireSRWLockExclusive(&superblock->lock);
|
|
superblock->cache_expiration = 0;
|
|
ReleaseSRWLockExclusive(&superblock->lock);
|
|
}
|