1 /* NFSv4.1 client for Windows 2 * Copyright � 2012 The Regents of the University of Michigan 3 * 4 * Olga Kornievskaia <aglo@umich.edu> 5 * Casey Bodley <cbodley@umich.edu> 6 * 7 * This library is free software; you can redistribute it and/or modify it 8 * under the terms of the GNU Lesser General Public License as published by 9 * the Free Software Foundation; either version 2.1 of the License, or (at 10 * your option) any later version. 11 * 12 * This library is distributed in the hope that it will be useful, but 13 * without any warranty; without even the implied warranty of merchantability 14 * or fitness for a particular purpose. See the GNU Lesser General Public 15 * License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public License 18 * along with this library; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 20 */ 21 22 #include <windows.h> 23 #include <stdio.h> 24 25 #include "daemon_debug.h" 26 #include "nfs41.h" 27 #include "nfs41_ops.h" 28 #include "from_kernel.h" 29 #include "util.h" 30 31 32 #define SBLVL 3 /* dprintf level for superblock logging */ 33 34 35 static __inline int compare_fsid( 36 IN const nfs41_fsid *lhs, 37 IN const nfs41_fsid *rhs) 38 { 39 if (lhs->major > rhs->major) return 1; 40 if (lhs->major < rhs->major) return -1; 41 if (lhs->minor > rhs->minor) return 1; 42 if (lhs->minor < rhs->minor) return -1; 43 return 0; 44 } 45 46 47 /* nfs41_superblock */ 48 static int superblock_create( 49 IN const nfs41_fsid *fsid, 50 OUT nfs41_superblock **superblock_out) 51 { 52 int status = NO_ERROR; 53 nfs41_superblock *superblock; 54 55 dprintf(SBLVL, "creating superblock for fsid(%llu,%llu)\n", 56 fsid->major, fsid->minor); 57 58 superblock = calloc(1, sizeof(nfs41_superblock)); 59 if (superblock == NULL) { 60 status = GetLastError(); 61 eprintf("failed to allocate superblock " 62 "for fsid(%llu,%llu)\n", fsid->major, fsid->minor); 63 goto out; 64 } 65 66 memcpy(&superblock->fsid, fsid, sizeof(nfs41_fsid)); 67 InitializeSRWLock(&superblock->lock); 68 69 *superblock_out = superblock; 70 out: 71 return status; 72 } 73 74 static int get_superblock_attrs( 75 IN nfs41_session *session, 76 IN nfs41_superblock *superblock, 77 IN nfs41_path_fh *file) 78 { 79 bool_t supports_named_attrs; 80 int status; 81 bitmap4 attr_request; 82 nfs41_file_info info = { 0 }; 83 84 attr_request.arr[0] = FATTR4_WORD0_SUPPORTED_ATTRS | 85 FATTR4_WORD0_LINK_SUPPORT | FATTR4_WORD0_SYMLINK_SUPPORT | 86 FATTR4_WORD0_ACLSUPPORT | FATTR4_WORD0_CANSETTIME | 87 FATTR4_WORD0_CASE_INSENSITIVE | FATTR4_WORD0_CASE_PRESERVING | 88 FATTR4_WORD0_MAXREAD | (uint32_t)(FATTR4_WORD0_MAXWRITE); 89 attr_request.arr[1] = FATTR4_WORD1_FS_LAYOUT_TYPE | 90 FATTR4_WORD1_TIME_DELTA; 91 attr_request.arr[2] = FATTR4_WORD2_SUPPATTR_EXCLCREAT; 92 attr_request.count = 3; 93 94 info.supported_attrs = &superblock->supported_attrs; 95 info.suppattr_exclcreat = &superblock->suppattr_exclcreat; 96 info.time_delta = &superblock->time_delta; 97 98 status = nfs41_superblock_getattr(session, file, 99 &attr_request, &info, &supports_named_attrs); 100 if (status) { 101 eprintf("nfs41_superblock_getattr() failed with %s when fetching " 102 "attributes for fsid(%llu,%llu)\n", nfs_error_string(status), 103 superblock->fsid.major, superblock->fsid.minor); 104 goto out; 105 } 106 107 if (info.maxread) 108 superblock->maxread = info.maxread; 109 else 110 superblock->maxread = session->fore_chan_attrs.ca_maxresponsesize; 111 112 if (info.maxwrite) 113 superblock->maxwrite = info.maxwrite; 114 else 115 superblock->maxwrite = session->fore_chan_attrs.ca_maxrequestsize; 116 117 superblock->layout_types = info.fs_layout_types; 118 superblock->aclsupport = info.aclsupport; 119 superblock->link_support = info.link_support; 120 superblock->symlink_support = info.symlink_support; 121 superblock->ea_support = supports_named_attrs; 122 superblock->case_preserving = info.case_preserving; 123 superblock->case_insensitive = info.case_insensitive; 124 125 if (bitmap_isset(&info.attrmask, 0, FATTR4_WORD0_CANSETTIME)) 126 superblock->cansettime = info.cansettime; 127 else /* cansettime is not supported, try setting them anyway */ 128 superblock->cansettime = 1; 129 130 /* if time_delta is not supported, default to 1s */ 131 if (!bitmap_isset(&info.attrmask, 1, FATTR4_WORD1_TIME_DELTA)) 132 superblock->time_delta.seconds = 1; 133 134 /* initialize the default getattr mask */ 135 superblock->default_getattr.count = 2; 136 superblock->default_getattr.arr[0] = FATTR4_WORD0_TYPE 137 | FATTR4_WORD0_CHANGE | FATTR4_WORD0_SIZE 138 | FATTR4_WORD0_FILEID | FATTR4_WORD0_HIDDEN 139 | FATTR4_WORD0_ARCHIVE; 140 superblock->default_getattr.arr[1] = FATTR4_WORD1_MODE 141 | FATTR4_WORD1_NUMLINKS | FATTR4_WORD1_SYSTEM 142 | FATTR4_WORD1_TIME_ACCESS | FATTR4_WORD1_TIME_CREATE 143 | FATTR4_WORD1_TIME_MODIFY; 144 superblock->default_getattr.arr[2] = 0; 145 146 nfs41_superblock_supported_attrs(superblock, &superblock->default_getattr); 147 148 dprintf(SBLVL, "attributes for fsid(%llu,%llu): " 149 "maxread=%llu, maxwrite=%llu, layout_types: 0x%X, " 150 "cansettime=%u, time_delta={%llu,%u}, aclsupport=%u, " 151 "link_support=%u, symlink_support=%u, case_preserving=%u, " 152 "case_insensitive=%u\n", 153 superblock->fsid.major, superblock->fsid.minor, 154 superblock->maxread, superblock->maxwrite, 155 superblock->layout_types, superblock->cansettime, 156 superblock->time_delta.seconds, superblock->time_delta.nseconds, 157 superblock->aclsupport, superblock->link_support, 158 superblock->symlink_support, superblock->case_preserving, 159 superblock->case_insensitive); 160 out: 161 return status; 162 } 163 164 void nfs41_superblock_fs_attributes( 165 IN const nfs41_superblock *superblock, 166 OUT PFILE_FS_ATTRIBUTE_INFORMATION FsAttrs) 167 { 168 FsAttrs->FileSystemAttributes = FILE_SUPPORTS_REMOTE_STORAGE; 169 if (superblock->link_support) 170 FsAttrs->FileSystemAttributes |= FILE_SUPPORTS_HARD_LINKS; 171 if (superblock->symlink_support) 172 FsAttrs->FileSystemAttributes |= FILE_SUPPORTS_REPARSE_POINTS; 173 if (superblock->ea_support) 174 FsAttrs->FileSystemAttributes |= FILE_SUPPORTS_EXTENDED_ATTRIBUTES; 175 if (superblock->case_preserving) 176 FsAttrs->FileSystemAttributes |= FILE_CASE_PRESERVED_NAMES; 177 if (!superblock->case_insensitive) 178 FsAttrs->FileSystemAttributes |= FILE_CASE_SENSITIVE_SEARCH; 179 if (superblock->aclsupport) 180 FsAttrs->FileSystemAttributes |= FILE_PERSISTENT_ACLS; 181 182 FsAttrs->MaximumComponentNameLength = NFS41_MAX_COMPONENT_LEN; 183 184 /* let the driver fill in FileSystemName */ 185 FsAttrs->FileSystemNameLength = 0; 186 187 dprintf(SBLVL, "FileFsAttributeInformation: case_preserving %u, " 188 "case_insensitive %u, max component %u\n", 189 superblock->case_preserving, superblock->case_insensitive, 190 FsAttrs->MaximumComponentNameLength); 191 } 192 193 194 /* nfs41_superblock_list */ 195 #define superblock_entry(pos) list_container(pos, nfs41_superblock, entry) 196 197 static int superblock_compare( 198 const struct list_entry *entry, 199 const void *value) 200 { 201 const nfs41_superblock *superblock = superblock_entry(entry); 202 return compare_fsid(&superblock->fsid, (const nfs41_fsid*)value); 203 } 204 205 static nfs41_superblock* find_superblock( 206 IN nfs41_superblock_list *superblocks, 207 IN const nfs41_fsid *fsid) 208 { 209 struct list_entry *entry; 210 entry = list_search(&superblocks->head, fsid, superblock_compare); 211 return entry ? superblock_entry(entry) : NULL; 212 } 213 214 void nfs41_superblock_list_init( 215 IN nfs41_superblock_list *superblocks) 216 { 217 list_init(&superblocks->head); 218 InitializeSRWLock(&superblocks->lock); 219 } 220 221 void nfs41_superblock_list_free( 222 IN nfs41_superblock_list *superblocks) 223 { 224 struct list_entry *entry, *tmp; 225 226 dprintf(SBLVL, "nfs41_superblock_list_free()\n"); 227 228 list_for_each_tmp(entry, tmp, &superblocks->head) 229 free(superblock_entry(entry)); 230 } 231 232 233 int nfs41_superblock_for_fh( 234 IN nfs41_session *session, 235 IN const nfs41_fsid *fsid, 236 IN const nfs41_fh *parent OPTIONAL, 237 OUT nfs41_path_fh *file) 238 { 239 int status = NFS4_OK; 240 nfs41_server *server = client_server(session->client); 241 nfs41_superblock_list *superblocks = &server->superblocks; 242 nfs41_superblock *superblock; 243 244 dprintf(SBLVL, "--> nfs41_superblock_for_fh(fsid(%llu,%llu))\n", 245 fsid->major, fsid->minor); 246 247 /* compare with the parent's fsid, and use that if it matches */ 248 if (parent && parent->superblock && 249 compare_fsid(fsid, &parent->superblock->fsid) == 0) { 250 file->fh.superblock = parent->superblock; 251 dprintf(SBLVL, "using superblock from parent\n"); 252 goto out; 253 } 254 255 /* using a shared lock, search for an existing superblock */ 256 AcquireSRWLockShared(&superblocks->lock); 257 superblock = find_superblock(superblocks, fsid); 258 ReleaseSRWLockShared(&superblocks->lock); 259 260 if (superblock) { 261 dprintf(SBLVL, "found existing superblock in server list " 262 "[shared lock]\n"); 263 } else { 264 AcquireSRWLockExclusive(&superblocks->lock); 265 /* must search again under an exclusive lock, in case another thread 266 * created it after our first search */ 267 superblock = find_superblock(superblocks, fsid); 268 if (superblock) { 269 dprintf(SBLVL, "found newly created superblock in server list " 270 "[exclusive lock]\n"); 271 } else { 272 /* create the superblock */ 273 status = superblock_create(fsid, &superblock); 274 if (status == NO_ERROR) /* add it to the list */ 275 list_add_tail(&superblocks->head, &superblock->entry); 276 } 277 ReleaseSRWLockExclusive(&superblocks->lock); 278 } 279 280 if (status == NO_ERROR && superblock->supported_attrs.count == 0) { 281 /* exclusive lock on the superblock while fetching attributes */ 282 AcquireSRWLockExclusive(&superblock->lock); 283 if (superblock->supported_attrs.count == 0) 284 status = get_superblock_attrs(session, superblock, file); 285 ReleaseSRWLockExclusive(&superblock->lock); 286 } 287 288 file->fh.superblock = superblock; 289 out: 290 dprintf(SBLVL, "<-- nfs41_superblock_for_fh() returning %p, status %d\n", 291 file->fh.superblock, status); 292 return status; 293 } 294 295 void nfs41_superblock_space_changed( 296 IN nfs41_superblock *superblock) 297 { 298 /* invalidate cached volume size attributes */ 299 AcquireSRWLockExclusive(&superblock->lock); 300 superblock->cache_expiration = 0; 301 ReleaseSRWLockExclusive(&superblock->lock); 302 } 303