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 
compare_fsid(IN const nfs41_fsid * lhs,IN const nfs41_fsid * rhs)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 */
superblock_create(IN const nfs41_fsid * fsid,OUT nfs41_superblock ** superblock_out)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 
get_superblock_attrs(IN nfs41_session * session,IN nfs41_superblock * superblock,IN nfs41_path_fh * file)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 
nfs41_superblock_fs_attributes(IN const nfs41_superblock * superblock,OUT PFILE_FS_ATTRIBUTE_INFORMATION FsAttrs)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 
superblock_compare(const struct list_entry * entry,const void * value)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 
find_superblock(IN nfs41_superblock_list * superblocks,IN const nfs41_fsid * fsid)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 
nfs41_superblock_list_init(IN nfs41_superblock_list * superblocks)214 void nfs41_superblock_list_init(
215     IN nfs41_superblock_list *superblocks)
216 {
217     list_init(&superblocks->head);
218     InitializeSRWLock(&superblocks->lock);
219 }
220 
nfs41_superblock_list_free(IN nfs41_superblock_list * superblocks)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 
nfs41_superblock_for_fh(IN nfs41_session * session,IN const nfs41_fsid * fsid,IN const nfs41_fh * parent OPTIONAL,OUT nfs41_path_fh * file)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 
nfs41_superblock_space_changed(IN nfs41_superblock * superblock)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