Adaptive Framework  0.9.0
All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Modules Pages
afw_vfs_adaptor_session.c
Go to the documentation of this file.
1 /* Interface afw_adaptor_session Implementation for afw_vfs */
2 // See the 'COPYING' file in the project root for licensing information.
3 /*
4  * Adaptive Framework
5  *
6  * Copyright (c) 2010-2023 Clemson University
7  *
8  */
9 
10 
16 #include "afw.h"
17 #include "afw_adaptor_impl.h"
19 #include <apr_fnmatch.h>
20 
21 
22 /* Declares and rti/inf defines for interface afw_adaptor_session */
23 #define AFW_IMPLEMENTATION_ID "vfs"
25 
26 
27 static const afw_key_z_string_z_t *
28 impl_get_vfs_entry(
30  const afw_utf8_t *object_id,
31  afw_xctx_t *xctx)
32 {
33  const afw_vfs_adaptor_internal_t *adaptor;
34  const afw_key_z_string_z_t *e;
35 
36  adaptor = (const afw_vfs_adaptor_internal_t * )self->pub.adaptor;
37  for (e = adaptor->vfs_map; e->key_z; e++) {
38  if (afw_utf8_starts_with(object_id, &e->key)) {
39  break;
40  }
41  }
42  if (e && !e->key.s) {
43  e = NULL;
44  }
45 
46  return e;
47 }
48 
49 
50 static const afw_object_t *
51 impl_read_file_object(
53  const afw_adaptor_impl_request_t *impl_request,
54  const afw_key_z_string_z_t *vfs_entry,
55  const afw_utf8_t *object_id,
56  const afw_pool_t *p,
57  afw_xctx_t *xctx)
58 {
59  const afw_object_t *object;
60  const afw_utf8_z_t *file_path_z;
61  const afw_utf8_t *data_string;
62  const afw_memory_t *data_binary;
63  const afw_utf8_t *vfs_path;
64  const afw_list_t *filenames;
65  afw_value_dateTime_t *dateTime;
66  afw_utf8_t adjusted;
67  FILE *fd;
68  afw_byte_t *buff;
69  apr_dir_t *dir;
70  afw_size_t size;
71  afw_size_t size_read;
72  apr_finfo_t finfo;
73  apr_status_t rv;
74 
75  object = NULL;
76  adjusted.s = object_id->s + vfs_entry->key.len;
77  adjusted.len = object_id->len - vfs_entry->key.len;
78  file_path_z = afw_utf8_z_printf(p, xctx,
79  "%" AFW_UTF8_FMT "%" AFW_UTF8_FMT,
80  AFW_UTF8_FMT_ARG(&vfs_entry->string),
81  AFW_UTF8_FMT_ARG(&adjusted));
82  rv = apr_stat(&finfo, file_path_z, APR_FINFO_MIN, afw_pool_get_apr_pool(p));
83  if (rv != APR_SUCCESS) {
84  /* If not found, return with NULL. */
85  if (APR_STATUS_IS_ENOENT(rv)) {
86  return NULL;
87  }
88  /*
89  If APR_INCOMPLETE is returned all the fields in finfo may not be
90  filled in, and you need to check the finfo->valid bitmask to verify
91  that what you're looking for is there.
92  */
93  AFW_THROW_ERROR_RV_Z(general,
94  apr, rv, "apr_stat() error", xctx);
95  }
96 
97  /* File type directory. */
98  if (finfo.filetype == APR_DIR) {
99 
100  /* Open directory. */
101  rv = apr_dir_open(&dir, file_path_z, afw_pool_get_apr_pool(p));
102 
103  /* If not found, return no objects. */
104  if (APR_STATUS_IS_ENOENT(rv)) {
105  return NULL;
106  }
107 
108  /* If there is another problem, throw error. */
109  if (rv != APR_SUCCESS) {
110  AFW_THROW_ERROR_RV_FZ(general, apr, rv, xctx,
111  "apr_dir_open() %s failed.",
112  vfs_entry->string_z);
113  }
114 
115  /* Process each file in directory. */
116  object = afw_object_create_and_cede_p(p, xctx);
118  &self->pub.adaptor->adaptor_id,
119  &afw_vfs_s__AdaptiveFile_vfs,
120  object_id,
121  xctx);
123  &afw_vfs_s_isDirectory, afw_value_true, xctx);
124  vfs_path = afw_utf8_printf(p, xctx,
125  "/%" AFW_UTF8_FMT "/%" AFW_UTF8_FMT,
126  AFW_UTF8_FMT_ARG(&self->pub.adaptor->adaptor_id),
127  AFW_UTF8_FMT_ARG(object_id));
129  &afw_vfs_s_vfsPath, vfs_path, xctx);
130  filenames = afw_list_of_create(afw_data_type_string, p, xctx);
132  &afw_vfs_s_data, filenames, xctx);
133  for (;;) {
134 
135  /* Read next directory entry until there are no more.*/
136  rv = apr_dir_read(&finfo, APR_FINFO_NAME | APR_FINFO_TYPE, dir);
137  if (rv == APR_ENOENT || rv == 720018) break;
139  if (rv != APR_SUCCESS) {
140  apr_dir_close(dir);
141  AFW_THROW_ERROR_RV_Z(general, apr, rv, "apr_dir_read() failed.",
142  xctx);
143  }
144 
145  /* Directory. */
146  if (finfo.filetype == APR_DIR) {
147  /* Skip ./ and ../ since those are never allowed in objectIds.*/
148  if (strcmp(finfo.name, ".") == 0 ||
149  strcmp(finfo.name, "..") == 0)
150  {
151  continue;
152  }
153  size = strlen(finfo.name) + 1;
154  buff = afw_pool_malloc(p, size, xctx);
155  buff[size - 1] = '/';
156  memcpy(buff, finfo.name, size - 1);
157  data_string = afw_utf8_create((const afw_utf8_octet_t *)buff, size, p, xctx);
158  afw_list_add_value(filenames,
159  afw_value_create_string(data_string, p, xctx),
160  xctx);
161  }
162 
163  /* Regular file. */
164  else if (finfo.filetype == APR_REG) {
165  size = strlen(finfo.name);
166  buff = afw_pool_malloc(p, size, xctx);
167  memcpy(buff, finfo.name, size);
168  data_string = afw_utf8_create((const afw_utf8_octet_t *)buff, size, p, xctx);
169  afw_list_add_value(filenames,
170  afw_value_create_string(data_string, p, xctx),
171  xctx);
172  }
173 
174  /* Ignore other filetypes. */
175  }
176 
177  /* Close ObjectType's directory. */
178  rv = apr_dir_close(dir);
179  if (rv != APR_SUCCESS) {
180  AFW_THROW_ERROR_RV_Z(general, apr, rv, "apr_dir_close() failed.",
181  xctx);
182  }
183  }
184 
185  /* File type regular file. */
186  else if (finfo.filetype == APR_REG) {
187  fd = fopen(file_path_z, "r");
188  if (fd)
189  {
190  size = (size_t)finfo.size;
191  buff = afw_pool_malloc(p, size, xctx);
192  size_read = fread(buff, 1, size, fd);
193  fclose(fd);
194  if (size_read == -1) {
195  AFW_THROW_ERROR_FZ(general, xctx,
196  "Error reading %s.", file_path_z);
197  }
198  object = afw_object_create_and_cede_p(p, xctx);
200  &self->pub.adaptor->adaptor_id,
201  &afw_vfs_s__AdaptiveFile_vfs,
202  object_id,
203  xctx);
204  vfs_path = afw_utf8_printf(p, xctx,
205  "/%" AFW_UTF8_FMT "/%" AFW_UTF8_FMT,
206  AFW_UTF8_FMT_ARG(&self->pub.adaptor->adaptor_id),
207  AFW_UTF8_FMT_ARG(object_id));
209  &afw_vfs_s_vfsPath, vfs_path, xctx);
210  if (afw_utf8_is_valid((const afw_utf8_octet_t *)buff, size_read, xctx)) {
211  data_string = afw_utf8_create((const afw_utf8_octet_t *)buff, size_read,
212  p, xctx);
214  &afw_vfs_s_data, data_string, xctx);
215  }
216  else {
217  data_binary = afw_memory_create(buff, size_read,
218  p, xctx);
220  &afw_vfs_s_data, data_binary, xctx);
221  }
222  }
223  }
224 
225  /* If file type wasn't ignore, add time properties to object. */
226  if (finfo.filetype == APR_DIR || finfo.filetype == APR_REG) {
227 
229  if (finfo.atime != 0) {
230  dateTime = afw_value_allocate_dateTime(object->p, xctx);
231  afw_dateTime_set_from_apr_time(&dateTime->internal,
232  finfo.atime, xctx);
234  &afw_vfs_s_timeAccessed,
235  (const afw_value_t *)dateTime,
236  xctx);
237  }
238 
240  if (finfo.ctime != 0) {
241  dateTime = afw_value_allocate_dateTime(object->p, xctx);
242  afw_dateTime_set_from_apr_time(&dateTime->internal,
243  finfo.ctime, xctx);
245  &afw_vfs_s_timeCreated,
246  (const afw_value_t *)dateTime,
247  xctx);
248  }
249 
251  if (finfo.mtime != 0) {
252  dateTime = afw_value_allocate_dateTime(object->p, xctx);
253  afw_dateTime_set_from_apr_time(&dateTime->internal,
254  finfo.mtime, xctx);
256  &afw_vfs_s_timeModified,
257  (const afw_value_t *)dateTime,
258  xctx);
259  }
260 
261  }
262 
263  /* Return object. */
264  return object;
265 }
266 
267 
268 
269 static afw_boolean_t
270 impl_process_directory(
273  const afw_key_z_string_z_t *vfs_entry,
274  afw_xctx_t *xctx)
275 {
276  const afw_pool_t *object_p;
277  const afw_object_t *object;
278  const afw_utf8_t *object_id;
279  afw_key_z_string_z_t subdirectory_vfs_entry;
280  apr_finfo_t finfo;
281  apr_dir_t *dir;
282  apr_status_t rv;
283  afw_boolean_t shortcut;
284 
285  shortcut = false;
286 
287  /* Open directory. */
288  rv = apr_dir_open(&dir, vfs_entry->string_z, afw_pool_get_apr_pool(ctx->p));
289 
290  /* If not found, return no objects. */
291  if (APR_STATUS_IS_ENOENT(rv)) {
292  return shortcut;
293  }
294 
295  /* If there is another problem, throw error. */
296  if (rv != APR_SUCCESS) {
297  AFW_THROW_ERROR_RV_FZ(general, apr, rv, xctx,
298  "apr_dir_open() %s failed.",
299  vfs_entry->string_z);
300  }
301 
302  /* Process each file in directory. */
303  for (;;) {
304  object = NULL;
305  object_p = NULL;
306 
307  /* Read next directory entry until there are no more.*/
308  afw_memory_clear(&finfo);
309  rv = apr_dir_read(&finfo, APR_FINFO_NAME | APR_FINFO_TYPE, dir);
310  if (rv == APR_ENOENT || rv == 720018) break;
312  if (rv != APR_SUCCESS) {
313  AFW_THROW_ERROR_RV_Z(general, apr, rv, "apr_dir_read() failed.",
314  xctx);
315  }
316 
317  /* Always skip ./ and ../ plus hidden files unless requested. */
318  if (*(finfo.name) == '.') {
319  if (!ctx->includeHidden ||
320  (finfo.filetype == APR_DIR &&
321  (strcmp(finfo.name, "./") == 0 ||
322  strcmp(finfo.name, "../") == 0)))
323  {
324  continue;
325  }
326  }
327 
328  /* If this is a directory and recursive is specified, traverse. */
329  if (ctx->recursive && finfo.filetype == APR_DIR) {
330  afw_memory_clear(&subdirectory_vfs_entry);
331  subdirectory_vfs_entry.key_z = afw_utf8_z_printf(
332  ctx->p, xctx, "%s%s/", vfs_entry->key_z, finfo.name);
333  subdirectory_vfs_entry.key.len =
334  strlen(subdirectory_vfs_entry.key_z);
335  subdirectory_vfs_entry.string_z = afw_utf8_z_printf(
336  ctx->p, xctx, "%s%s/", vfs_entry->string_z, finfo.name);
337  subdirectory_vfs_entry.string.len =
338  strlen(subdirectory_vfs_entry.string_z);
339  shortcut = impl_process_directory(self,
340  ctx, &subdirectory_vfs_entry, xctx);
341  if (shortcut) {
342  return shortcut;
343  }
344  }
345 
346  /* Otherwise ... */
347  else {
348 
349  /* If regular file that doesn't have correct suffix, skip it. */
350  if (finfo.filetype == APR_REG && ctx->suffix) {
351  if (strlen(finfo.name) < ctx->suffix->len ||
352  memcmp(
353  finfo.name +
354  strlen(finfo.name) -
355  ctx->suffix->len,
356  ctx->suffix->s,
357  ctx->suffix->len
358  ) != 0)
359  {
360  continue;
361  }
362  }
363 
364  /* Read file object. Object has its own pool. */
365  object_p = afw_pool_create(ctx->p, xctx);
366  object_id = afw_utf8_printf(object_p, xctx,
367  "%s%s%s", vfs_entry->key_z, finfo.name,
368  finfo.filetype == APR_DIR ? "/" : "");
369  object = impl_read_file_object(self, ctx->impl_request, vfs_entry,
370  object_id, object_p, xctx);
371  }
372 
373  /*
374  * If query criteria met, callback with object. Callback will
375  * release object. If callback returns true, prematurely stop
376  * retrieving.
377  */
378  if (object &&
379  afw_query_criteria_test_object(object, ctx->criteria,
380  ctx->p, xctx))
381  {
382  shortcut = ctx->original_callback(object,
383  ctx->original_context, xctx);
384  if (shortcut) {
385  break;
386  }
387  }
388 
389  /* If query criteria not met, release pool. */
390  else if (object_p) {
391  afw_pool_release(object_p, xctx);
392  }
393  }
394 
395  /* Close ObjectType's directory. */
396  rv = apr_dir_close(dir);
397  if (rv != APR_SUCCESS) {
398  AFW_THROW_ERROR_RV_Z(general, apr, rv, "apr_dir_close() failed.",
399  xctx);
400  }
401 
402  return shortcut;
403 }
404 
405 
406 
407 /* Internal session create */
408 const afw_adaptor_session_t *
409 afw_vfs_adaptor_internal_session_create(
411  afw_xctx_t *xctx)
412 {
414 
415  /*
416  * You may want to create a new pool for instance, but will just use
417  * xctx's pool in this example.
418  */
419  self = afw_xctx_calloc_type(
421  self->pub.inf = &impl_afw_adaptor_session_inf;
422  self->pub.adaptor = (const afw_adaptor_t *)adaptor;
423  self->pub.p = xctx->p;
424 
425  /* Return new instance. */
426  return (afw_adaptor_session_t *)self;
427 }
428 
429 
430 
431 /*
432  * Implementation of method destroy for interface afw_adaptor_session.
433  */
434 void
436  const afw_adaptor_session_t * instance,
437  afw_xctx_t *xctx)
438 {
439  /* Nothing to do. */
440 }
441 
442 
443 
444 /*
445  * Implementation of method retrieve_objects for interface
446  * afw_adaptor_session.
447  */
448 void
450  const afw_adaptor_session_t *instance,
451  const afw_adaptor_impl_request_t *impl_request,
452  const afw_utf8_t *object_type_id,
453  const afw_query_criteria_t *criteria,
454  void *context,
455  afw_object_cb_t callback,
456  const afw_object_t *adaptor_type_specific,
457  const afw_pool_t *p,
458  afw_xctx_t *xctx)
459 {
462  const afw_vfs_adaptor_internal_t *adaptor =
463  (const afw_vfs_adaptor_internal_t *)self->pub.adaptor;
465  const afw_key_z_string_z_t *vfs_entry;
466  const afw_utf8_t *subdirectory;
467  const afw_object_t *object;
468  afw_key_z_string_z_t subdirectory_vfs_entry;
469  afw_boolean_t found;
470  afw_boolean_t short_circuit;
471 
472  subdirectory = NULL;
473  afw_memory_clear(&ctx);
474  ctx.p = p;
475  ctx.impl_request = impl_request;
476  ctx.criteria = criteria;
477  ctx.original_context = context;
478  ctx.original_callback = callback;
479  ctx.suffix = &afw_s_a_empty_string;
480 
481  /* Object type _AdaptiveObjectType_ */
482  if (afw_utf8_equal(object_type_id, &afw_s__AdaptiveObjectType_)) {
483  short_circuit = false;
484  object = afw_runtime_get_object(
485  &afw_s__AdaptiveObjectType_,
486  &afw_vfs_s__AdaptiveAdaptorTypeSpecific_vfs_retrieve_objects,
487  xctx);
488  if (object &&
489  afw_query_criteria_test_object(object, criteria, p, xctx))
490  {
491  short_circuit = callback(object, context, xctx);
492  }
493 
494  if (!short_circuit) {
495  object = afw_runtime_get_object(
496  &afw_s__AdaptiveObjectType_,
497  &afw_vfs_s__AdaptiveFile_vfs,
498  xctx);
499  if (object &&
500  afw_query_criteria_test_object(object, criteria, p, xctx))
501  {
502  short_circuit = callback(object, context, xctx);
503  }
504  }
505 
506  callback(NULL, context, xctx);
507  return;
508  }
509 
510  /* There are no other object types other than _AdaptiveFile_vfs. */
511  if (!afw_utf8_equal(object_type_id, &afw_vfs_s__AdaptiveFile_vfs)) {
512  callback(NULL, context, xctx);
513  return;
514  }
515 
516  /* Adaptor vfs specific properties. */
517  if (adaptor_type_specific) {
518 
519  /* includeHidden */
520  ctx.includeHidden = afw_object_old_get_property_as_boolean(
521  adaptor_type_specific, &afw_vfs_s_includeHidden, &found, xctx);
522 
523  /* subdirectory */
525  adaptor_type_specific, &afw_vfs_s_subdirectory, xctx);
526  if (subdirectory &&
527  (
528  afw_utf8_starts_with(subdirectory, &afw_s_a_slash) ||
529  !afw_utf8_ends_with(subdirectory, &afw_s_a_slash) ||
530  afw_utf8_contains(subdirectory, &afw_s_a_backslash) ||
531  afw_utf8_contains(subdirectory, &afw_s_a_dot_slash)
532  ))
533  {
534  AFW_THROW_ERROR_Z(general,
535  "The subdirectory property of the adaptorTypeSpecific "
536  "parameter of retrieve_objects() can not start with '/', "
537  "must end with '/', and can not contain '\\', './' or '../'",
538  xctx);
539  }
540 
541  /* suffix */
543  adaptor_type_specific, &afw_vfs_s_suffix, xctx);
544 
545  /* recursive */
547  adaptor_type_specific, &afw_vfs_s_recursive, &found, xctx);
548  }
549 
550  /* Process each map entry applying subdirectory if specified. */
551  for (vfs_entry = adaptor->vfs_map; vfs_entry->key_z; vfs_entry++)
552  {
553  if (subdirectory) {
554  subdirectory_vfs_entry.key_z = afw_utf8_z_printf(p, xctx,
555  "%" AFW_UTF8_FMT "%" AFW_UTF8_FMT,
556  AFW_UTF8_FMT_ARG(&vfs_entry->key),
557  AFW_UTF8_FMT_ARG(subdirectory));
558  subdirectory_vfs_entry.key.len =
559  strlen(subdirectory_vfs_entry.key_z);
560  subdirectory_vfs_entry.string_z = afw_utf8_z_printf(p, xctx,
561  "%" AFW_UTF8_FMT "%" AFW_UTF8_FMT,
562  AFW_UTF8_FMT_ARG(&vfs_entry->string),
563  AFW_UTF8_FMT_ARG(subdirectory));
564  subdirectory_vfs_entry.string.len =
565  strlen(subdirectory_vfs_entry.string_z);
566  }
567  else {
568  afw_memory_copy(&subdirectory_vfs_entry, vfs_entry);
569  }
570  impl_process_directory(self, &ctx, &subdirectory_vfs_entry, xctx);
571  }
572 
573  /* Call callback one more time with NULL object pointer. */
574  callback(NULL, context, xctx);
575 }
576 
577 
578 
579 /*
580  * Implementation of method get_object for interface afw_adaptor_session.
581  */
582 void
584  const afw_adaptor_session_t *instance,
585  const afw_adaptor_impl_request_t *impl_request,
586  const afw_utf8_t *object_type_id,
587  const afw_utf8_t *object_id,
588  void *context,
589  afw_object_cb_t callback,
590  const afw_object_t *adaptor_type_specific,
591  const afw_pool_t *p,
592  afw_xctx_t *xctx)
593 {
596  const afw_object_t *object;
597  const afw_pool_t *object_p;
598  const afw_key_z_string_z_t *vfs_entry;
599 
600  /* Only object type _AdaptiveFile_vfs is supported. */
601  if (!afw_utf8_equal(object_type_id, &afw_vfs_s__AdaptiveFile_vfs)) {
602  callback(NULL, context, xctx);
603  return;
604  }
605 
606  /* Don't allow ./, ../, or \ in object id. */
607  if (afw_utf8_contains(object_id, &afw_s_a_dot_slash) ||
608  afw_utf8_contains(object_id, &afw_s_a_backslash))
609  {
610  callback(NULL, context, xctx);
611  return;
612  }
613 
614  /* Get object or NULL and pass it to callback. */
615  object = NULL;
616  vfs_entry = impl_get_vfs_entry(self, object_id, xctx);
617  if (vfs_entry) {
618  object_p = afw_pool_create(p, xctx);
619  object = impl_read_file_object(self, impl_request,
620  vfs_entry, object_id, object_p, xctx);
621  if (!object) {
622  afw_pool_release(object_p, xctx);
623  }
624  }
625  callback(object, context, xctx);
626 }
627 
628 
629 
630 static void
631 impl_determine_path_for_object_id(
633  const afw_utf8_t *object_id,
634  afw_boolean_t expect_exists,
635  afw_utf8_utf8_z_t *path,
636  afw_boolean_t *is_directory,
637  const afw_pool_t *p,
638  afw_xctx_t *xctx)
639 {
640  const afw_key_z_string_z_t *vfs_entry;
641  afw_utf8_utf8_z_t adjusted;
642  apr_finfo_t finfo;
643  afw_boolean_t exists;
644  apr_status_t rv;
645 
646  /* Initialize return parameters. */
647  path->s.s = NULL;
648  path->s.len = 0;
649  *is_directory = false;
650 
651  /* Get vfs entry. */
652  vfs_entry = impl_get_vfs_entry(self, object_id, xctx);
653  if (!vfs_entry) {
654  AFW_THROW_ERROR_Z(not_found,
655  "object_id does not match anything in vfs map",
656  xctx);
657  }
658 
659  /* If object_id ended with '/', consider it a directory. */
660  if (afw_utf8_ends_with(object_id, &afw_s_a_slash)) {
661  *is_directory = true;
662  }
663 
664  /* Construct path. */
665  adjusted.s.s = object_id->s + vfs_entry->key.len;
666  adjusted.s.len = object_id->len - vfs_entry->key.len;
667  path->s_z = afw_utf8_z_printf(p, xctx,
668  "%" AFW_UTF8_FMT "%" AFW_UTF8_FMT,
669  AFW_UTF8_FMT_ARG(&vfs_entry->string),
670  AFW_UTF8_FMT_ARG(&adjusted.s));
671  path->s.len = strlen(path->s_z);
672 
673  /* Get if path exists and make sure is_directory is correct. */
674  rv = apr_stat(&finfo, path->s_z, APR_FINFO_TYPE, afw_pool_get_apr_pool(p));
675  exists = true;
676  if (APR_STATUS_IS_ENOENT(rv)) {
677  exists = false;
678  }
679  else if (rv != APR_SUCCESS) {
680  AFW_THROW_ERROR_RV_Z(general,
681  apr, rv, "apr_stat() error", xctx);
682  }
683 
684  /* Check expect_exists */
685  if (expect_exists) {
686  if (!exists) {
687  AFW_THROW_ERROR_FZ(not_found, xctx,
688  "object_id %" AFW_UTF8_FMT " doesn't exist",
689  AFW_UTF8_FMT_OPTIONAL_ARG(object_id));
690  }
691  }
692  else {
693  if (exists) {
694  AFW_THROW_ERROR_FZ(general, xctx,
695  "object_id %" AFW_UTF8_FMT " already exists",
696  AFW_UTF8_FMT_OPTIONAL_ARG(object_id));
697  }
698  }
699 
700  /* If exists, make sure filetype acceptable. */
701  if (exists) {
702  if (finfo.filetype == APR_DIR) {
703  if (!*is_directory) {
704  AFW_THROW_ERROR_FZ(general, xctx,
705  "object_id %" AFW_UTF8_FMT
706  " is directory so must end with '/'",
707  AFW_UTF8_FMT_OPTIONAL_ARG(object_id));
708  }
709  }
710  else if (finfo.filetype == APR_REG) {
711  if (*is_directory) {
712  AFW_THROW_ERROR_FZ(general, xctx,
713  "object_id %" AFW_UTF8_FMT
714  " is a regular file so must not end with '/'",
715  AFW_UTF8_FMT_OPTIONAL_ARG(object_id));
716  }
717  }
718  else {
719  AFW_THROW_ERROR_FZ(general, xctx,
720  "object_id %" AFW_UTF8_FMT
721  " filetype is not allowed",
722  AFW_UTF8_FMT_OPTIONAL_ARG(object_id));
723  }
724  }
725 }
726 
727 
728 static void
729 impl_write_data_to_file(
731  const afw_utf8_t *object_id,
732  const afw_utf8_z_t *path_z,
733  const afw_value_t *data,
734  afw_boolean_t is_create,
735  afw_xctx_t *xctx)
736 {
737  const afw_vfs_adaptor_internal_t *adaptor =
738  (const afw_vfs_adaptor_internal_t *)self->pub.adaptor;
739  const afw_utf8_utf8_z_t *pattern;
740  const afw_utf8_z_t *vfs_path_z;
741  const void *buf;
742  apr_size_t nbytes;
743  apr_size_t bytes_written;
744  apr_file_t *fd;
745  apr_int32_t flag;
746  apr_status_t rv;
747 
748  if (!data ||
749  (!afw_value_is_string(data) && !afw_value_is_hexBinary(data)))
750  {
751  AFW_THROW_ERROR_Z(general,
752  "object must have a \"data\" property that is data type "
753  "string or hexBinary for vfs adaptor",
754  xctx);
755  }
756 
757  /* Flag is write/create plus binary if not string data. */
758  flag = APR_FOPEN_WRITE | APR_FOPEN_CREATE;
759  if (!afw_value_is_string(data)) {
760  flag |= APR_FOPEN_BINARY;
761  }
762 
763  /* If is create, set that in flag. */
764  if (is_create) {
765  flag |= APR_FOPEN_CREATE;
766  }
767 
768  /* Open file. */
769  rv = apr_file_open(&fd, path_z, flag, APR_FPROT_OS_DEFAULT,
770  afw_pool_get_apr_pool(xctx->p));
771  if (rv != APR_SUCCESS) {
772  AFW_THROW_ERROR_RV_Z(general, apr, rv,
773  "apr_file_open() error",
774  xctx);
775  }
776 
777  /* Write full file. */
778  if (afw_value_is_string(data)) {
779  buf = (const void *)((const afw_value_string_t *)data)->internal.s;
780  nbytes = (apr_size_t)((const afw_value_string_t *)data)->internal.len;
781  }
782  else {
783  buf = (const void *)((const afw_value_hexBinary_t *)data)->internal.ptr;
784  nbytes = (apr_size_t)((const afw_value_hexBinary_t *)data)->internal.size;
785  }
786  rv = apr_file_write_full(fd, buf, nbytes, &bytes_written);
787  apr_file_close(fd);
788  if (rv != APR_SUCCESS) {
789  AFW_THROW_ERROR_RV_Z(general, apr, rv,
790  "apr_file_write_full() error",
791  xctx);
792  }
793 
794  /* Make file executable if match in adaptor->mark_executable. */
795  if (adaptor->mark_executable) {
796  vfs_path_z = afw_utf8_z_printf(self->pub.p, xctx,
797  "/%" AFW_UTF8_FMT "/%" AFW_UTF8_FMT,
798  AFW_UTF8_FMT_ARG(&adaptor->pub.adaptor_id),
799  AFW_UTF8_FMT_ARG(object_id));
800  for (pattern = adaptor->mark_executable; pattern->s_z; pattern++) {
801  if (apr_fnmatch(pattern->s_z, vfs_path_z, 0) ==
802  APR_SUCCESS)
803  {
804  rv = apr_file_attrs_set(path_z,
805  APR_FILE_ATTR_EXECUTABLE, APR_FILE_ATTR_EXECUTABLE,
806  afw_pool_get_apr_pool(xctx->p));
807  if (rv != APR_SUCCESS && rv != APR_ENOTIMPL) {
808  AFW_THROW_ERROR_RV_Z(general, apr, rv,
809  "apr_file_attrs_set() error",
810  xctx);
811  }
812  break;
813  }
814  }
815  }
816 }
817 
818 
819 
820 /*
821  * Implementation of method add_object for interface afw_adaptor_session.
822  */
823 const afw_utf8_t *
825  const afw_adaptor_session_t *instance,
826  const afw_adaptor_impl_request_t *impl_request,
827  const afw_utf8_t *object_type_id,
828  const afw_utf8_t *suggested_object_id,
829  const afw_object_t *object,
830  const afw_object_t *adaptor_type_specific,
831  afw_xctx_t *xctx)
832 {
835  afw_utf8_utf8_z_t path;
836  const afw_value_t *data;
837  apr_status_t rv;
838  afw_boolean_t is_directory;
839 
840  /* Only object type _AdaptiveFile_vfs is allowed. */
841  if (!afw_utf8_equal(object_type_id, &afw_vfs_s__AdaptiveFile_vfs)) {
842  AFW_THROW_ERROR_Z(general,
843  "Only object type _AdaptiveFile_vfs is allowed by vfs adaptor",
844  xctx);
845  }
846 
847  /* Determine path and check existence. */
848  impl_determine_path_for_object_id(
849  self, suggested_object_id, false,
850  &path, &is_directory,
851  xctx->p, xctx);
852 
853 
854  /* If directory, make it recursively. Property data is ignored. */
855  if (is_directory) {
856 
857  /* Make directory.*/
858  rv = apr_dir_make_recursive(path.s_z, APR_FPROT_OS_DEFAULT,
859  afw_pool_get_apr_pool(xctx->p));
860  if (rv != APR_SUCCESS) {
861  AFW_THROW_ERROR_RV_Z(general, apr, rv,
862  "apr_dir_make_recursive() error",
863  xctx);
864  }
865  }
866 
867  /* If regular file, get data property and write to file. */
868  else {
869  data = afw_object_get_property(object, &afw_vfs_s_data, xctx);
870  impl_write_data_to_file(self,
871  suggested_object_id, path.s_z, data, true, xctx);
872  }
873 
874  /* The suggested_object_id is the actual one. */
875  return suggested_object_id;
876 }
877 
878 
879 
880 /*
881  * Implementation of method modify_object for interface afw_adaptor_session.
882  */
883 void
885  const afw_adaptor_session_t *instance,
886  const afw_adaptor_impl_request_t *impl_request,
887  const afw_utf8_t *object_type_id,
888  const afw_utf8_t *object_id,
889  const afw_adaptor_modify_entry_t *const *entry,
890  const afw_object_t *adaptor_type_specific,
891  afw_xctx_t *xctx)
892 {
895  afw_utf8_utf8_z_t path;
896  afw_boolean_t is_directory;
897  afw_boolean_t valid;
898 
899 
900  /* Only object type _AdaptiveFile_vfs is allowed. */
901  if (!afw_utf8_equal(object_type_id, &afw_vfs_s__AdaptiveFile_vfs)) {
902  AFW_THROW_ERROR_Z(general,
903  "Only object type _AdaptiveFile_vfs is allowed by vfs adaptor",
904  xctx);
905  }
906 
907  /* Determine path and check existence. */
908  impl_determine_path_for_object_id(
909  self, object_id, true,
910  &path, &is_directory,
911  xctx->p, xctx);
912 
913  /* Can't modify a directory. */
914  if (is_directory) {
915  AFW_THROW_ERROR_Z(general,
916  "modify_object() is not allowed for a directory by vfs adaptor",
917  xctx);
918  }
919 
920  /* Only [["set_property", "data", "<file context>"]] is supported. */
921  if (!*entry) {
922  return;
923  }
924  valid = true;
925  if ((*entry)->type != afw_adaptor_modify_entry_type_set_property) {
926  valid = false;
927  }
928  else if (!(*entry)->first_property_name_entry ||
929  (*entry)->first_property_name_entry->next)
930  {
931  valid = false;
932  }
933  else if (
935  &(*entry)->first_property_name_entry->property_name,
936  &afw_vfs_s_data))
937  {
938  valid = false;
939  }
940  else if (*(entry + 1)) {
941  valid = false;
942  }
943  if (!valid) {
944  AFW_THROW_ERROR_Z(general,
945  "modify_object() only supports "
946  "[[\"set_property\", \"data\", value]] for vfs adaptor",
947  xctx);
948  }
949 
950  /* Write data to file. */
951  impl_write_data_to_file(self,
952  object_id, path.s_z, (*entry)->value, false, xctx);
953 }
954 
955 
956 
957 /*
958  * Implementation of method replace_object for interface afw_adaptor_session.
959  */
960 void
962  const afw_adaptor_session_t *instance,
963  const afw_adaptor_impl_request_t *impl_request,
964  const afw_utf8_t *object_type_id,
965  const afw_utf8_t *object_id,
966  const afw_object_t *replacement_object,
967  const afw_object_t *adaptor_type_specific,
968  afw_xctx_t *xctx)
969 {
972  afw_utf8_utf8_z_t path;
973  const afw_value_t *data;
974  afw_boolean_t is_directory;
975 
976  /* Only object type _AdaptiveFile_vfs is allowed. */
977  if (!afw_utf8_equal(object_type_id, &afw_vfs_s__AdaptiveFile_vfs)) {
978  AFW_THROW_ERROR_Z(general,
979  "Only object type _AdaptiveFile_vfs is allowed by vfs adaptor",
980  xctx);
981  }
982 
983  /* Determine path and check existence. */
984  impl_determine_path_for_object_id(
985  self, object_id, true,
986  &path, &is_directory,
987  xctx->p, xctx);
988 
989  /* Can't replace a directory. */
990  if (is_directory) {
991  AFW_THROW_ERROR_Z(general,
992  "replace_object() is not allowed for a directory by vfs adaptor",
993  xctx);
994  }
995 
996  /* Write data property and write to file. */
997  data = afw_object_get_property(replacement_object, &afw_vfs_s_data, xctx);
998  impl_write_data_to_file(self,
999  object_id, path.s_z, data, false, xctx);
1000 }
1001 
1002 
1003 
1004 /*
1005  * Implementation of method delete_object for interface afw_adaptor_session.
1006  */
1007 void
1009  const afw_adaptor_session_t *instance,
1010  const afw_adaptor_impl_request_t *impl_request,
1011  const afw_utf8_t *object_type_id,
1012  const afw_utf8_t *object_id,
1013  const afw_object_t *adaptor_type_specific,
1014  afw_xctx_t *xctx)
1015 {
1018  afw_utf8_utf8_z_t path;
1019  apr_status_t rv;
1020  afw_boolean_t is_directory;
1021 
1022  /* Only object type _AdaptiveFile_vfs is allowed. */
1023  if (!afw_utf8_equal(object_type_id, &afw_vfs_s__AdaptiveFile_vfs)) {
1024  AFW_THROW_ERROR_Z(general,
1025  "Only object type _AdaptiveFile_vfs is allowed",
1026  xctx);
1027  }
1028 
1029  /* Determine path and check existence. */
1030  impl_determine_path_for_object_id(
1031  self, object_id, true,
1032  &path, &is_directory,
1033  xctx->p, xctx);
1034 
1035  /* If directory, remove it. */
1036  if (is_directory) {
1037  rv = apr_dir_remove(path.s_z, afw_pool_get_apr_pool(xctx->p));
1038  if (rv != APR_SUCCESS) {
1039  AFW_THROW_ERROR_RV_Z(general, apr, rv,
1040  "apr_dir_remove() error",
1041  xctx);
1042  }
1043  }
1044 
1045  /* If regular file, remove it. */
1046  else {
1047  rv = apr_file_remove(path.s_z, afw_pool_get_apr_pool(xctx->p));
1048  if (rv != APR_SUCCESS) {
1049  AFW_THROW_ERROR_RV_Z(general, apr, rv,
1050  "apr_file_remove() error",
1051  xctx);
1052  }
1053  }
1054 }
1055 
1056 
1057 
1058 /*
1059  * Implementation of method begin_transaction for interface
1060  * afw_adaptor_session.
1061  */
1064  const afw_adaptor_session_t * instance,
1065  afw_xctx_t *xctx)
1066 {
1067  return NULL;
1068 }
1069 
1070 
1071 
1072 /*
1073  * Implementation of method get_journal_interface for interface
1074  * afw_adaptor_session.
1075  */
1076 const afw_adaptor_journal_t *
1077 impl_afw_adaptor_session_get_journal_interface(
1078  const afw_adaptor_session_t * instance,
1079  afw_xctx_t *xctx)
1080 {
1081  return NULL;
1082 }
1083 
1084 
1085 
1086 /*
1087  * Implementation of method get_key_value_interface for interface
1088  * afw_adaptor_session.
1089  */
1092  const afw_adaptor_session_t * instance,
1093  afw_xctx_t *xctx)
1094 {
1095  return NULL;
1096 }
1097 
1098 
1099 
1100 /*
1101  * Implementation of method get_index_interface for interface
1102  * afw_adaptor_session.
1103  */
1106  const afw_adaptor_session_t * instance,
1107  afw_xctx_t *xctx)
1108 {
1109  return NULL;
1110 }
1111 
1112 
1113 
1114 /*
1115  * Implementation of method get_object_type_cache_interface for interface
1116  * afw_adaptor_session.
1117  */
1119 impl_afw_adaptor_session_get_object_type_cache_interface(
1120  const afw_adaptor_session_t * instance,
1121  afw_xctx_t *xctx)
1122 {
1123  return NULL;
1124 }
Adaptive Framework Core API.
Helpers for adaptor implementation development.
Interface afw_interface implementation declares.
Internal header for adaptor type vfs.
void impl_afw_adaptor_session_modify_object(const afw_adaptor_session_t *instance, const afw_adaptor_impl_request_t *impl_request, const afw_utf8_t *object_type_id, const afw_utf8_t *object_id, const afw_adaptor_modify_entry_t *const *entry, const afw_object_t *adaptor_type_specific, afw_xctx_t *xctx)
void impl_afw_adaptor_session_replace_object(const afw_adaptor_session_t *instance, const afw_adaptor_impl_request_t *impl_request, const afw_utf8_t *object_type_id, const afw_utf8_t *object_id, const afw_object_t *replacement_object, const afw_object_t *adaptor_type_specific, afw_xctx_t *xctx)
void impl_afw_adaptor_session_delete_object(const afw_adaptor_session_t *instance, const afw_adaptor_impl_request_t *impl_request, const afw_utf8_t *object_type_id, const afw_utf8_t *object_id, const afw_object_t *adaptor_type_specific, afw_xctx_t *xctx)
void impl_afw_adaptor_session_retrieve_objects(const afw_adaptor_session_t *instance, const afw_adaptor_impl_request_t *impl_request, const afw_utf8_t *object_type_id, const afw_query_criteria_t *criteria, void *context, afw_object_cb_t callback, const afw_object_t *adaptor_type_specific, const afw_pool_t *p, afw_xctx_t *xctx)
const afw_adaptor_transaction_t * impl_afw_adaptor_session_begin_transaction(const afw_adaptor_session_t *instance, afw_xctx_t *xctx)
void impl_afw_adaptor_session_destroy(const afw_adaptor_session_t *instance, afw_xctx_t *xctx)
const afw_adaptor_impl_index_t * impl_afw_adaptor_session_get_index_interface(const afw_adaptor_session_t *instance, afw_xctx_t *xctx)
const afw_utf8_t * impl_afw_adaptor_session_add_object(const afw_adaptor_session_t *instance, const afw_adaptor_impl_request_t *impl_request, const afw_utf8_t *object_type_id, const afw_utf8_t *suggested_object_id, const afw_object_t *object, const afw_object_t *adaptor_type_specific, afw_xctx_t *xctx)
void impl_afw_adaptor_session_get_object(const afw_adaptor_session_t *instance, const afw_adaptor_impl_request_t *impl_request, const afw_utf8_t *object_type_id, const afw_utf8_t *object_id, void *context, afw_object_cb_t callback, const afw_object_t *adaptor_type_specific, const afw_pool_t *p, afw_xctx_t *xctx)
const afw_adaptor_key_value_t * impl_afw_adaptor_session_get_key_value_interface(const afw_adaptor_session_t *instance, afw_xctx_t *xctx)
afw_object_set_property_as_anyURI(const afw_object_t *object, const afw_utf8_t *property_name, const afw_utf8_t *internal, afw_xctx_t *xctx)
Set property function for data type anyURI values.
#define afw_object_old_get_property_as_boolean(object, property_name, found, xctx)
Get property function for data type boolean value.
afw_value_allocate_dateTime(const afw_pool_t *p, afw_xctx_t *xctx)
Allocate function for unmanaged data type dateTime value.
#define afw_value_is_hexBinary(A_VALUE)
Macro to determine if value is evaluated hexBinary.
afw_object_set_property_as_hexBinary(const afw_object_t *object, const afw_utf8_t *property_name, const afw_memory_t *internal, afw_xctx_t *xctx)
Set property function for data type hexBinary values.
afw_object_set_property_as_list(const afw_object_t *object, const afw_utf8_t *property_name, const afw_list_t *internal, afw_xctx_t *xctx)
Set property function for data type list values.
afw_value_create_string(const afw_utf8_t *internal, const afw_pool_t *p, afw_xctx_t *xctx)
Create function for unmanaged data type string value.
#define afw_object_old_get_property_as_string(object, property_name, xctx)
Get property function for data type string value.
afw_data_type_string
Data type struct for string.
#define afw_value_is_string(A_VALUE)
Macro to determine if value is evaluated string.
afw_object_set_property_as_string(const afw_object_t *object, const afw_utf8_t *property_name, const afw_utf8_t *internal, afw_xctx_t *xctx)
Set property function for data type string values.
#define AFW_UTF8_FMT_ARG(A_STRING)
Convenience Macro for use with AFW_UTF8_FMT to specify arg.
Definition: afw_common.h:605
afw_boolean_t(* afw_object_cb_t)(const afw_object_t *object, void *context, afw_xctx_t *xctx)
Typedef for afw_adaptor_session_object callback.
Definition: afw_common.h:1176
#define AFW_UTF8_FMT_OPTIONAL_ARG(A_STRING)
Convenience Macro for use with AFW_UTF8_FMT to specify optional arg.
Definition: afw_common.h:616
_Bool afw_boolean_t
Definition: afw_common.h:373
unsigned char afw_byte_t
A byte of memory (unsigned).
Definition: afw_common.h:208
#define AFW_UTF8_FMT
Format string specifier used for afw_utf8_t.
Definition: afw_common.h:588
afw_utf8_octet_t afw_utf8_z_t
NFC normalized UTF-8 null terminated string.
Definition: afw_common.h:523
char afw_utf8_octet_t
8 bits of utf-8 codepoint.
Definition: afw_common.h:236
apr_size_t afw_size_t
size_t.
Definition: afw_common.h:151
#define AFW_THROW_ERROR_RV_Z(code, rv_source_id, rv, message_z, xctx)
Macro used to set error and rv in xctx and throw it.
Definition: afw_error.h:301
#define AFW_THROW_ERROR_FZ(code, xctx, format_z,...)
Macro used to set error and 0 rv in xctx and throw it.
Definition: afw_error.h:319
#define AFW_THROW_ERROR_RV_FZ(code, rv_source_id, rv, xctx, format_z,...)
Macro used to set error and rv in xctx and throw it.
Definition: afw_error.h:338
#define AFW_THROW_ERROR_Z(code, message_z, xctx)
Macro used to set error and 0 rv in xctx and throw it.
Definition: afw_error.h:283
afw_list_add_value(const afw_list_t *instance, const afw_value_t *value, afw_xctx_t *xctx)
Call method add_value of interface afw_list_setter.
Definition: afw_list.c:104
#define afw_list_of_create(data_type, p, xctx)
Create an list of a specific data type in memory.
Definition: afw_list.h:64
#define afw_memory_clear(to)
Clear preallocated memory for sizeof(*(to)).
Definition: afw_memory.h:47
const afw_memory_t * afw_memory_create(const afw_byte_t *ptr, afw_size_t size, const afw_pool_t *p, afw_xctx_t *xctx)
Create a afw_memory_t struct for a ptr and size.
Definition: afw_memory.h:61
#define afw_memory_copy(to, from)
Copy to preallocated memory of same type.
Definition: afw_memory.h:39
#define afw_object_get_property(instance, property_name, xctx)
Call method get_property of interface afw_object.
afw_object_meta_set_ids(const afw_object_t *instance, const afw_utf8_t *adaptor_id, const afw_utf8_t *object_type_id, const afw_utf8_t *object_id, afw_xctx_t *xctx)
Set object's ids.
afw_object_set_property(const afw_object_t *instance, const afw_utf8_t *property_name, const afw_value_t *value, afw_xctx_t *xctx)
Set the value of an object's property.
Definition: afw_object.c:46
#define afw_object_create_and_cede_p(p, xctx)
Create an empty entity object in memory in specified pool and cede control of the pool to the object.
Definition: afw_object.h:898
#define afw_pool_malloc(instance, size, xctx)
Call method malloc of interface afw_pool.
#define afw_pool_get_apr_pool(instance)
Call method get_apr_pool of interface afw_pool.
#define afw_pool_release(instance, xctx)
Call method release of interface afw_pool.
const afw_pool_t * afw_pool_create(const afw_pool_t *parent, afw_xctx_t *xctx)
Create a new pool.
afw_query_criteria_test_object(const afw_object_t *obj, const afw_query_criteria_t *criteria, const afw_pool_t *p, afw_xctx_t *xctx)
Test object against query criteria.
afw_runtime_get_object(const afw_utf8_t *object_type_id, const afw_utf8_t *object_id, afw_xctx_t *xctx)
Get a runtime object.
Definition: afw_runtime.c:853
void afw_dateTime_set_from_apr_time(afw_dateTime_t *dateTime, apr_time_t apr_time, afw_xctx_t *xctx)
Set afw_dateTime_t from apr_time.
Definition: afw_time.c:304
afw_utf8_ends_with(const afw_utf8_t *string, const afw_utf8_t *ends_with)
Check to see if a string ends with another string.
Definition: afw_utf8.c:513
afw_boolean_t afw_utf8_equal(const afw_utf8_t *s1, const afw_utf8_t *s2)
Check to see if a string equals another string.
const afw_utf8_z_t * afw_utf8_z_printf(const afw_pool_t *p, afw_xctx_t *xctx, const afw_utf8_z_t *format_z,...)
Definition: afw_utf8.h:854
afw_utf8_printf(const afw_pool_t *p, afw_xctx_t *xctx, const afw_utf8_z_t *format,...)
Create a utf-8 string using a c format string in specified pool.
Definition: afw_utf8.c:459
afw_utf8_contains(const afw_utf8_t *s1, const afw_utf8_t *s2)
Check to see if a string contains another string.
Definition: afw_utf8.c:543
afw_boolean_t afw_utf8_starts_with(const afw_utf8_t *string, const afw_utf8_t *starts_with)
Check to see if a string starts with another string.
#define afw_utf8_is_valid(s, len, xctx)
Determine if series of bytes is valid utf-8.
Definition: afw_utf8.h:154
#define afw_utf8_create(s, len, p, xctx)
Create utf-8 string without copy unless necessary in pool specified.
Definition: afw_utf8.h:239
afw_value_true
Adaptive value true.
Definition: afw_value.h:348
#define afw_xctx_calloc_type(type, xctx)
Macro to allocate cleared memory to hold type in xctx's pool.
Definition: afw_xctx.h:199
Interface afw_adaptor_impl_index public struct.
Internal request info used by afw_adaptor_impl*() functions.
Interface afw_adaptor_journal public struct.
Interface afw_adaptor_key_value public struct.
Adaptor modify entry.
Interface afw_adaptor_object_type_cache public struct.
Interface afw_adaptor public struct.
Interface afw_adaptor_session public struct.
Interface afw_adaptor_transaction public struct.
Typedef for key/string pair that have both utf8 and utf8_z.
Definition: afw_common.h:698
Interface afw_list public struct.
Struct for memory pointer and size.
Definition: afw_common.h:505
Interface afw_object public struct.
Interface afw_pool public struct.
Parsed query criteria.
NFC normalized UTF-8 string.
Definition: afw_common.h:545
struct for data type dateTime values.
struct for data type hexBinary values.
Interface afw_value public struct.
struct for data type string values.
Interface afw_xctx public struct.
NFC normalized UTF-8 string accessible as afw_utf8_t or afw_utf8_z_t.
Definition: afw_common.h:555