1 /*
2 * vim:noexpandtab:shiftwidth=8:tabstop=8:
3 *
4 * Copyright 2017-2019 Red Hat, Inc.
5 * Author: Daniel Gryniewicz dang@redhat.com
6 *
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 3 of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 * 02110-1301 USA
22 *
23 * -------------
24 */
25
26 /* export.c
27 * MEM FSAL export object
28 */
29
30 #include "config.h"
31
32 #include "fsal.h"
33 #include <libgen.h> /* used for 'dirname' */
34 #include <pthread.h>
35 #include <string.h>
36 #include <sys/types.h>
37 #include <os/mntent.h>
38 #include <os/quota.h>
39 #include <dlfcn.h>
40 #include "fsal_convert.h"
41 #include "FSAL/fsal_commonlib.h"
42 #include "FSAL/fsal_config.h"
43 #include "mem_int.h"
44 #include "nfs_exports.h"
45 #include "nfs_core.h"
46 #include "export_mgr.h"
47
48 #ifdef __FreeBSD__
49 #include <sys/endian.h>
50
51 #define bswap_16(x) bswap16((x))
52 #define bswap_64(x) bswap64((x))
53 #endif
54
55 #ifdef USE_LTTNG
56 #include "gsh_lttng/fsal_mem.h"
57 #endif
58 /* helpers to/from other MEM objects
59 */
60
61 /* export object methods
62 */
63
64 static void mem_release_export(struct fsal_export *exp_hdl)
65 {
66 struct mem_fsal_export *myself;
67
68 myself = container_of(exp_hdl, struct mem_fsal_export, export);
69
70 if (myself->root_handle != NULL) {
71 mem_clean_export(myself->root_handle);
72
73 fsal_obj_handle_fini(&myself->root_handle->obj_handle);
74
75 LogDebug(COMPONENT_FSAL,
76 "Releasing hdl=%p, name=%s",
77 myself->root_handle, myself->root_handle->m_name);
78
79 PTHREAD_RWLOCK_wrlock(&myself->mfe_exp_lock);
80 mem_free_handle(myself->root_handle);
81 PTHREAD_RWLOCK_unlock(&myself->mfe_exp_lock);
82
83 myself->root_handle = NULL;
84 }
85
86 fsal_detach_export(exp_hdl->fsal, &exp_hdl->exports);
87 free_export_ops(exp_hdl);
88
89 glist_del(&myself->export_entry);
90
91 gsh_free(myself->export_path);
92 gsh_free(myself);
93 }
94
95 static fsal_status_t mem_get_dynamic_info(struct fsal_export *exp_hdl,
96 struct fsal_obj_handle *obj_hdl,
97 fsal_dynamicfsinfo_t *infop)
98 {
99 infop->total_bytes = 0;
100 infop->free_bytes = 0;
101 infop->avail_bytes = 0;
102 infop->total_files = 0;
103 infop->free_files = 0;
104 infop->avail_files = 0;
105 infop->time_delta.tv_sec = 1;
106 infop->time_delta.tv_nsec = 0;
107
108 return fsalstat(ERR_FSAL_NO_ERROR, 0);
109 }
110
111 /* extract a file handle from a buffer.
112 * do verification checks and flag any and all suspicious bits.
113 * Return an updated fh_desc into whatever was passed. The most
114 * common behavior, done here is to just reset the length. There
115 * is the option to also adjust the start pointer.
116 */
117
118 static fsal_status_t mem_wire_to_host(struct fsal_export *exp_hdl,
119 fsal_digesttype_t in_type,
120 struct gsh_buffdesc *fh_desc,
121 int flags)
122 {
123 size_t fh_min;
124 uint64_t *hashkey;
125 ushort *len;
126
127 fh_min = 1;
128
129 if (fh_desc->len < fh_min) {
130 LogMajor(COMPONENT_FSAL,
131 "Size mismatch for handle. should be >= %zu, got %zu",
132 fh_min, fh_desc->len);
133 return fsalstat(ERR_FSAL_SERVERFAULT, 0);
134 }
135 hashkey = (uint64_t *)fh_desc->addr;
136 len = (ushort *)((char *)hashkey + sizeof(uint64_t));
137 if (flags & FH_FSAL_BIG_ENDIAN) {
138 #if (BYTE_ORDER != BIG_ENDIAN)
139 *len = bswap_16(*len);
140 *hashkey = bswap_64(*hashkey);
141 #endif
142 } else {
143 #if (BYTE_ORDER == BIG_ENDIAN)
144 *len = bswap_16(*len);
145 *hashkey = bswap_64(*hashkey);
146 #endif
147 }
148 return fsalstat(ERR_FSAL_NO_ERROR, 0);
149 }
150
151 /**
152 * @brief Allocate a state_t structure
153 *
154 * Note that this is not expected to fail since memory allocation is
155 * expected to abort on failure.
156 *
157 * @param[in] exp_hdl Export state_t will be associated with
158 * @param[in] state_type Type of state to allocate
159 * @param[in] related_state Related state if appropriate
160 *
161 * @returns a state structure.
162 */
163
164 static struct state_t *mem_alloc_state(struct fsal_export *exp_hdl,
165 enum state_type state_type,
166 struct state_t *related_state)
167 {
168 struct state_t *state;
169
170 state = init_state(gsh_calloc(1, sizeof(struct state_t)
171 + sizeof(struct fsal_fd)),
172 exp_hdl, state_type, related_state);
173 #ifdef USE_LTTNG
174 tracepoint(fsalmem, mem_alloc_state, __func__, __LINE__, state);
175 #endif
176 return state;
177 }
178
179 /* mem_export_ops_init
180 * overwrite vector entries with the methods that we support
181 */
182
183 void mem_export_ops_init(struct export_ops *ops)
184 {
185 ops->release = mem_release_export;
186 ops->lookup_path = mem_lookup_path;
187 ops->wire_to_host = mem_wire_to_host;
188 ops->create_handle = mem_create_handle;
189 ops->get_fs_dynamic_info = mem_get_dynamic_info;
190 ops->alloc_state = mem_alloc_state;
191 }
192
193 const char *str_async_type(uint32_t async_type)
194 {
195 switch (async_type) {
196 case MEM_INLINE:
197 return "INLINE";
198 case MEM_RANDOM_OR_INLINE:
199 return "RANDOM_OR_INLINE";
200 case MEM_RANDOM:
201 return "RANDOM";
202 case MEM_FIXED:
203 return "FIXED";
204 }
205
206 return "UNKNOWN";
207 }
208
209 static struct config_item_list async_types_conf[] = {
210 CONFIG_LIST_TOK("inline", MEM_INLINE),
211 CONFIG_LIST_TOK("fixed", MEM_FIXED),
212 CONFIG_LIST_TOK("random", MEM_RANDOM),
213 CONFIG_LIST_TOK("random_or_inline", MEM_RANDOM_OR_INLINE),
214 CONFIG_LIST_EOL
215 };
216
217 static struct config_item mem_export_params[] = {
218 CONF_ITEM_NOOP("name"),
219 CONF_ITEM_UI32("Async_Delay", 0, 1000, 0,
220 mem_fsal_export, async_delay),
221 CONF_ITEM_TOKEN("Async_Type", MEM_INLINE, async_types_conf,
222 mem_fsal_export, async_type),
223 CONF_ITEM_UI32("Async_Stall_Delay", 0, 1000, 0,
224 mem_fsal_export, async_stall_delay),
225 CONFIG_EOL
226 };
227
228 static struct config_block mem_export_param_block = {
229 .dbus_interface_name = "org.ganesha.nfsd.config.fsal.mem-export%d",
230 .blk_desc.name = "FSAL",
231 .blk_desc.type = CONFIG_BLOCK,
232 .blk_desc.u.blk.init = noop_conf_init,
233 .blk_desc.u.blk.params = mem_export_params,
234 .blk_desc.u.blk.commit = noop_conf_commit
235 };
236
237 /* create_export
238 * Create an export point and return a handle to it to be kept
239 * in the export list.
240 * First lookup the fsal, then create the export and then put the fsal back.
241 * returns the export with one reference taken.
242 */
243
244 fsal_status_t mem_create_export(struct fsal_module *fsal_hdl,
245 void *parse_node,
246 struct config_error_type *err_type,
247 const struct fsal_up_vector *up_ops)
248 {
249 struct mem_fsal_export *myself;
250 int retval = 0;
251 pthread_rwlockattr_t attrs;
252 fsal_status_t fsal_status = {0, 0};
253
254 myself = gsh_calloc(1, sizeof(struct mem_fsal_export));
255
256 glist_init(&myself->mfe_objs);
257 pthread_rwlockattr_init(&attrs);
258 #ifdef GLIBC
259 pthread_rwlockattr_setkind_np(&attrs,
260 PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP);
261 #endif
262 PTHREAD_RWLOCK_init(&myself->mfe_exp_lock, &attrs);
263 pthread_rwlockattr_destroy(&attrs);
264 fsal_export_init(&myself->export);
265 mem_export_ops_init(&myself->export.exp_ops);
266
267 retval = load_config_from_node(parse_node,
268 &mem_export_param_block,
269 myself,
270 true,
271 err_type);
272
273 if (retval != 0) {
274 fsal_status = posix2fsal_status(EINVAL);
275 goto err_free; /* seriously bad */
276 }
277
278 retval = fsal_attach_export(fsal_hdl, &myself->export.exports);
279
280 if (retval != 0) {
281 /* seriously bad */
282 LogMajor(COMPONENT_FSAL,
283 "Could not attach export");
284 fsal_status = posix2fsal_status(retval);
285 goto err_free; /* seriously bad */
286 }
287
288 myself->export.fsal = fsal_hdl;
289 myself->export.up_ops = up_ops;
290
291 /* Save the export path. */
292 myself->export_path = gsh_strdup(op_ctx->ctx_export->fullpath);
293 op_ctx->fsal_export = &myself->export;
294
295 /* Insert into exports list */
296 glist_add_tail(&MEM.mem_exports, &myself->export_entry);
297
298 LogDebug(COMPONENT_FSAL,
299 "Created exp %p - %s",
300 myself, myself->export_path);
301
302 return fsalstat(ERR_FSAL_NO_ERROR, 0);
303
304 err_free:
305 free_export_ops(&myself->export);
306 gsh_free(myself); /* elvis has left the building */
307 return fsal_status;
308 }
309
310 /**
311 * @brief Update an existing export
312 *
313 * This will result in a temporary fsal_export being created, and built into
314 * a stacked export.
315 *
316 * On entry, op_ctx has the original gsh_export and no fsal_export.
317 *
318 * The caller passes the original fsal_export, as well as the new super_export's
319 * FSAL when there is a stacked export. This will allow the underlying export to
320 * validate that the stacking has not changed.
321 *
322 * This function does not actually create a new fsal_export, the only purpose is
323 * to validate and update the config.
324 *
325 * @param[in] fsal_hdl FSAL module
326 * @param[in] parse_node opaque pointer to parse tree node for
327 * export options to be passed to
328 * load_config_from_node
329 * @param[out] err_type config proocessing error reporting
330 * @param[in] original The original export that is being updated
331 * @param[in] updated_super The updated super_export's FSAL
332 *
333 * @return FSAL status.
334 */
335
336 fsal_status_t mem_update_export(struct fsal_module *fsal_hdl,
337 void *parse_node,
338 struct config_error_type *err_type,
339 struct fsal_export *original,
340 struct fsal_module *updated_super)
341 {
342 struct mem_fsal_export myself;
343 int retval = 0;
344 struct mem_fsal_export *orig =
345 container_of(original, struct mem_fsal_export, export);
346 fsal_status_t status;
347
348 /* Check for changes in stacking by calling default update_export. */
349 status = update_export(fsal_hdl, parse_node, err_type,
350 original, updated_super);
351
352 if (FSAL_IS_ERROR(status))
353 return status;
354
355 memset(&myself, 0, sizeof(myself));
356
357 retval = load_config_from_node(parse_node,
358 &mem_export_param_block,
359 &myself,
360 true,
361 err_type);
362
363 if (retval != 0) {
364 return posix2fsal_status(EINVAL);
365 }
366
367 /* Update the async parameters */
368 atomic_store_uint32_t(&orig->async_delay, myself.async_delay);
369 atomic_store_uint32_t(&orig->async_stall_delay,
370 myself.async_stall_delay);
371 atomic_store_uint32_t(&orig->async_type, myself.async_type);
372
373 LogEvent(COMPONENT_FSAL,
374 "Updated FSAL_MEM aync parameters type=%s, delay=%"PRIu32
375 ", stall_delay=%"PRIu32,
376 str_async_type(myself.async_type),
377 myself.async_delay, myself.async_stall_delay);
378
379 return fsalstat(ERR_FSAL_NO_ERROR, 0);
380 }
381