src/server/test/vfs.c

changeset 385
a1f4cb076d2f
parent 323
dc5b0fee49df
child 415
d938228c382e
equal deleted inserted replaced
210:21274e5950af 385:a1f4cb076d2f
1 /*
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3 *
4 * Copyright 2019 Olaf Wintermann. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <errno.h>
32
33 #include <ucx/string.h>
34 #include <ucx/list.h>
35 #include <ucx/map.h>
36
37 #include "../daemon/session.h"
38
39 #include "testutils.h"
40
41 #include "vfs.h"
42
43 typedef struct TestVFS {
44 UcxMap *files;
45 int count_unlink;
46 int count_rmdir;
47 } TestVFS;
48
49 typedef struct TestVFSFile {
50 VFSFile file;
51 sstr_t path;
52 int isdir;
53 UcxBuffer *content;
54 } TestVFSFile;
55
56 typedef struct TestVFSDir {
57 VFSDir dir;
58 TestVFSFile *file;
59 UcxMapIterator i;
60 sstr_t name;
61 } TestVFSDir;
62
63 /* dir io */
64
65 static char* test_resource_name(char *url) {
66 sstr_t urlstr = sstr(url);
67 if(urlstr.ptr[urlstr.length-1] == '/') {
68 urlstr.length--;
69 }
70 sstr_t resname = sstrrchr(urlstr, '/');
71 if(resname.length > 1) {
72 return resname.ptr+1;
73 } else {
74 return url;
75 }
76 }
77
78 int testvfs_readdir(VFS_DIR dir, VFS_ENTRY *entry, int getstat) {
79 TestVFS *vfs = dir->ctx->vfs->instance;
80 TestVFSDir *vfsdir = (TestVFSDir*)dir;
81
82 sstr_t prefix = sstrcat(2, vfsdir->file->path, S("/"));
83
84 TestVFSFile *file = NULL;
85 UCX_MAP_FOREACH(key, file, vfsdir->i) {
86 sstr_t file_path = sstrcat(
87 2,
88 prefix,
89 sstr(test_resource_name(file->path.ptr)));
90 void *m = ucx_map_get(vfs->files, ucx_key(file_path.ptr, file_path.length));
91 // don't ask why alfree and not free()
92 alfree(ucx_default_allocator(), file_path.ptr);
93 if(m) {
94 break;
95 } else {
96 file = NULL;
97 }
98 }
99 free(prefix.ptr);
100
101 if(file) {
102 vfsdir->name = sstrdup_a(
103 session_get_allocator(dir->ctx->sn),
104 sstr(test_resource_name(file->path.ptr)));
105 ZERO(entry, sizeof(VFS_ENTRY));
106 entry->name = vfsdir->name.ptr;
107
108 if(getstat) {
109 ZERO(&entry->stat, sizeof(struct stat));
110 if(file->isdir) {
111 entry->stat.st_mode = S_IFDIR;
112 }
113 }
114
115 return 1;
116 } else {
117 return 0;
118 }
119 }
120
121 void testvfs_dir_close(VFS_DIR dir) {
122 TestVFSDir *testdir = (TestVFSDir*)dir;
123 pool_free(testdir->dir.ctx->sn->pool, dir);
124
125 }
126
127 ssize_t testvfs_read(SYS_FILE fd, void *buf, size_t nbyte) {
128 TestVFSFile *file = (TestVFSFile*)fd;
129 return (ssize_t)ucx_buffer_read(buf, 1, nbyte, file->content);
130 }
131
132 ssize_t testvfs_write(SYS_FILE fd, const void *buf, size_t nbyte) {
133 TestVFSFile *file = (TestVFSFile*)fd;
134 return (ssize_t)ucx_buffer_write(buf, 1, nbyte, file->content);
135 }
136
137 ssize_t testvfs_pread(SYS_FILE fd, void *buf, size_t nbyte, off_t offset) {
138 TestVFSFile *file = (TestVFSFile*)fd;
139 file->content->pos = (size_t)offset;
140 return testvfs_read(fd, buf, nbyte);
141 }
142
143 ssize_t testvfs_pwrite(SYS_FILE fd, const void *buf, size_t nbyte, off_t offset) {
144 TestVFSFile *file = (TestVFSFile*)fd;
145 file->content->pos = (size_t)offset;
146 return testvfs_write(fd, buf, nbyte);
147 }
148
149 off_t testvfs_seek(SYS_FILE fd, off_t offset, int whence) {
150 TestVFSFile *file = (TestVFSFile*)fd;
151 ucx_buffer_seek(file->content, offset, whence);
152 return (off_t)file->content->pos;
153 }
154
155 void testvfs_close(SYS_FILE fd) {
156 TestVFSFile *file = (TestVFSFile*)fd;
157 file->content->pos = 0;
158 }
159
160 VFS_IO test_file_io = {
161 testvfs_read,
162 testvfs_write,
163 testvfs_pread,
164 testvfs_pwrite,
165 testvfs_seek,
166 testvfs_close,
167 NULL, /* aio_read */
168 NULL /* aio_write */
169 };
170
171 VFS_DIRIO test_dir_io = {
172 testvfs_readdir,
173 testvfs_dir_close
174 };
175
176
177 /* vfs funcs */
178
179 SYS_FILE testvfs_open(VFSContext *ctx, const char *path, int oflags) {
180 TestVFS *vfs = ctx->vfs->instance;
181 TestVFSFile *file = NULL;
182
183 sstr_t s_path = sstr((char*)path);
184 if(sstrsuffix(s_path, S("/"))) {
185 s_path.length--;
186 }
187
188 file = ucx_map_sstr_get(vfs->files, s_path);
189 if(!file) {
190 if((oflags & O_CREAT) == O_CREAT) {
191 file = pool_malloc(ctx->sn->pool, sizeof(TestVFSFile));
192 ZERO(file, sizeof(TestVFSFile));
193 file->file.ctx = ctx;
194 file->path = sstrdup_a(session_get_allocator(ctx->sn), s_path);
195 file->file.io = &test_file_io;
196
197 file->content = pool_calloc(ctx->sn->pool, 1, sizeof(UcxBuffer));
198 file->content->capacity = 2048;
199 file->content->space = pool_malloc(ctx->sn->pool, file->content->capacity);
200 file->content->flags = 0;
201 file->content->pos = 0;
202 file->content->size = 0;
203
204 ucx_map_sstr_put(vfs->files, s_path, file);
205 } else {
206 ctx->vfs_errno = ENOENT;
207 }
208 }
209
210 return (SYS_FILE)file;
211 }
212
213 int testvfs_stat(VFSContext *ctx, const char *path, struct stat *buf) {
214 TestVFS *vfs = ctx->vfs->instance;
215 TestVFSFile *file = NULL;
216
217 sstr_t s_path = sstr((char*)path);
218 if(sstrsuffix(s_path, S("/"))) {
219 s_path.length--;
220 }
221
222 file = ucx_map_sstr_get(vfs->files, s_path);
223 if(!file) {
224 ctx->vfs_errno = ENOENT;
225 return 1;
226 }
227
228 ZERO(buf, sizeof(struct stat));
229 if(file->isdir) {
230 buf->st_mode = S_IFDIR;
231 }
232
233 return 0;
234 }
235
236 int testvfs_fstat(VFSContext *ctx, SYS_FILE fd, struct stat *buf) {
237 return 0;
238 }
239
240 VFS_DIR testvfs_opendir(VFSContext *ctx, const char *path) {
241 TestVFS *vfs = ctx->vfs->instance;
242 TestVFSFile *file = NULL;
243
244 sstr_t s_path = sstr((char*)path);
245 if(sstrsuffix(s_path, S("/"))) {
246 s_path.length--;
247 }
248
249 file = ucx_map_sstr_get(vfs->files, s_path);
250 if(!file) {
251 ctx->vfs_errno = ENOENT;
252 return NULL;
253 }
254
255 if(!file->isdir) {
256 return NULL;
257 }
258
259 TestVFSDir *dir = pool_malloc(ctx->sn->pool, sizeof(TestVFSDir));
260 ZERO(dir, sizeof(TestVFSDir));
261 dir->file = file;
262 dir->i = ucx_map_iterator(vfs->files);
263
264 dir->dir.ctx = ctx;
265 dir->dir.io = &test_dir_io;
266
267 return (VFS_DIR)dir;
268 }
269
270 VFS_DIR testvfs_fdopendir(VFSContext *ctx, SYS_FILE fd) {
271 TestVFS *vfs = ctx->vfs->instance;
272 TestVFSFile *file = (TestVFSFile*)fd;
273 if(!file->isdir) {
274 return NULL;
275 }
276
277 TestVFSDir *dir = pool_malloc(ctx->sn->pool, sizeof(TestVFSDir));
278 ZERO(dir, sizeof(TestVFSDir));
279 dir->file = file;
280 dir->i = ucx_map_iterator(vfs->files);
281
282 dir->dir.ctx = ctx;
283 dir->dir.io = &test_dir_io;
284
285 return (VFS_DIR)dir;
286 }
287
288 int testvfs_mkdir(VFSContext *ctx, const char *path) {
289 SYS_FILE fd = testvfs_open(ctx, path, O_CREAT);
290 if(!fd) {
291 return 1;
292 }
293
294 TestVFSFile *file = (TestVFSFile*)fd;
295 file->isdir = 1;
296
297 return 0;
298 }
299
300 int testvfs_unlink(VFSContext *ctx, const char *path) {
301 TestVFS *vfs = ctx->vfs->instance;
302 TestVFSFile *file = ucx_map_cstr_get(vfs->files, path);
303 if(!file) {
304 return 1;
305 }
306
307 if(file->isdir) {
308 return 1;
309 }
310
311 ucx_map_cstr_remove(vfs->files, path);
312 vfs->count_unlink++;
313 return 0;
314 }
315
316 int testvfs_rmdir(VFSContext *ctx, const char *path) {
317 TestVFS *vfs = ctx->vfs->instance;
318 TestVFSFile *dir = ucx_map_cstr_get(vfs->files, path);
319 if(!dir) {
320 ctx->vfs_errno = ENOENT;
321 return 1;
322 }
323
324 if(!dir->isdir) {
325 return 1;
326 }
327
328 UcxMapIterator i = ucx_map_iterator(vfs->files);
329 TestVFSFile *f;
330 UCX_MAP_FOREACH(key, f, i) {
331 if(f->path.length > dir->path.length && sstrprefix(f->path, dir->path)){
332 return 1; // dir not empty
333 }
334 }
335
336 ucx_map_cstr_remove(vfs->files, path);
337 vfs->count_rmdir++;
338 return 0;
339 }
340
341 static VFS testVFSClass = {
342 testvfs_open,
343 testvfs_stat,
344 testvfs_fstat,
345 testvfs_opendir,
346 testvfs_fdopendir,
347 testvfs_mkdir,
348 testvfs_unlink,
349 testvfs_rmdir,
350 0,
351 NULL
352 };
353
354
355 VFS* testvfs_create(Session *sn) {
356 TestVFS *vfs = pool_malloc(sn->pool, sizeof(TestVFS));
357 vfs->count_unlink = 0;
358 vfs->count_rmdir = 0;
359 vfs->files = ucx_map_new_a(session_get_allocator(sn), 64);
360
361 testVFSClass.instance = vfs;
362 return &testVFSClass;
363 }
364
365
366 /* ------------------------------------------------------------------------- */
367 //
368 // VFS Tests
369 //
370 /* ------------------------------------------------------------------------- */
371
372 UCX_TEST(test_vfs_open) {
373 Session *sn = testutil_session();
374 Request *rq = testutil_request(sn->pool, "PUT", "/");
375 rq->vfs = testvfs_create(sn);
376
377 VFSContext *vfs = vfs_request_context(sn, rq);
378
379 UCX_TEST_BEGIN;
380
381 UCX_TEST_ASSERT(vfs, "vfs is NULL");
382
383 SYS_FILE f1 = vfs_open(vfs, "/file1", O_CREAT);
384 UCX_TEST_ASSERT(f1, "f1 not opened");
385
386 SYS_FILE f2 = vfs_open(vfs, "/file1", 0);
387 UCX_TEST_ASSERT(f2, "f2 not opened");
388
389 UCX_TEST_END;
390
391 testutil_destroy_session(sn);
392 }
393
394 UCX_TEST(test_vfs_mkdir) {
395 Session *sn = testutil_session();
396 Request *rq = testutil_request(sn->pool, "PUT", "/");
397 rq->vfs = testvfs_create(sn);
398
399 VFSContext *vfs = vfs_request_context(sn, rq);
400
401 UCX_TEST_BEGIN;
402
403 int err = vfs_mkdir(vfs, "/dir");
404 UCX_TEST_ASSERT(err == 0, "error not 0");
405
406 SYS_FILE fd = vfs_open(vfs, "/dir", 0);
407 UCX_TEST_ASSERT(fd, "no fd");
408
409 UCX_TEST_END;
410
411 testutil_destroy_session(sn);
412 }
413
414 UCX_TEST(test_vfs_opendir) {
415 Session *sn = testutil_session();
416 Request *rq = testutil_request(sn->pool, "PUT", "/");
417 rq->vfs = testvfs_create(sn);
418
419 VFSContext *vfs = vfs_request_context(sn, rq);
420
421 UCX_TEST_BEGIN;
422
423 int err = vfs_mkdir(vfs, "/dir");
424 UCX_TEST_ASSERT(err == 0, "error not 0");
425
426 VFSDir *dir = vfs_opendir(vfs, "/dir");
427 UCX_TEST_ASSERT(dir, "no dir");
428
429 UCX_TEST_END;
430
431 testutil_destroy_session(sn);
432 }
433
434 UCX_TEST(test_vfs_readdir) {
435 Session *sn = testutil_session();
436 Request *rq = testutil_request(sn->pool, "PUT", "/");
437 rq->vfs = testvfs_create(sn);
438
439 VFSContext *vfs = vfs_request_context(sn, rq);
440
441 UCX_TEST_BEGIN;
442
443 int err = vfs_mkdir(vfs, "/dir");
444 UCX_TEST_ASSERT(err == 0, "error not 0");
445
446 // add some test file to /dir
447 UCX_TEST_ASSERT(vfs_open(vfs, "/dir/file1", O_CREAT), "creation of file1 failed");
448 UCX_TEST_ASSERT(vfs_open(vfs, "/dir/file2", O_CREAT), "creation of file2 failed");
449 UCX_TEST_ASSERT(vfs_open(vfs, "/dir/file3", O_CREAT), "creation of file3 failed");
450 UCX_TEST_ASSERT(vfs_open(vfs, "/dir/file4", O_CREAT), "creation of file4 failed");
451
452 VFSDir *dir = vfs_opendir(vfs, "/dir");
453 UCX_TEST_ASSERT(dir, "dir not opened");
454
455 UcxMap *files = ucx_map_new(8);
456
457 VFSEntry entry;
458 while(vfs_readdir(dir, &entry)) {
459 ucx_map_cstr_put(files, entry.name, dir);
460 }
461
462 UCX_TEST_ASSERT(files->count == 4, "wrong files count");
463 UCX_TEST_ASSERT(ucx_map_cstr_get(files, "file1"), "file1 missing");
464 UCX_TEST_ASSERT(ucx_map_cstr_get(files, "file2"), "file2 missing");
465 UCX_TEST_ASSERT(ucx_map_cstr_get(files, "file3"), "file3 missing");
466 UCX_TEST_ASSERT(ucx_map_cstr_get(files, "file4"), "file4 missing");
467
468 ucx_map_free(files);
469
470 UCX_TEST_END;
471
472 testutil_destroy_session(sn);
473 }
474
475 UCX_TEST(test_vfs_unlink) {
476 Session *sn = testutil_session();
477 Request *rq = testutil_request(sn->pool, "PUT", "/");
478 rq->vfs = testvfs_create(sn);
479
480 VFSContext *vfs = vfs_request_context(sn, rq);
481
482 UCX_TEST_BEGIN;
483 // prepare test
484 int err;
485 err = vfs_mkdir(vfs, "/dir1");
486 UCX_TEST_ASSERT(err == 0, "mkdir 1: error not 0");
487 err = vfs_mkdir(vfs, "/dir2");
488 UCX_TEST_ASSERT(err == 0, "mkdir 1: error not 0");
489
490 SYS_FILE f1 = vfs_open(vfs, "/file1", O_CREAT);
491 UCX_TEST_ASSERT(f1, "f1 not opened");
492
493 SYS_FILE f2 = vfs_open(vfs, "/file2", O_CREAT);
494 UCX_TEST_ASSERT(f1, "f2 not opened");
495
496 SYS_FILE f3 = vfs_open(vfs, "/dir1/file3", O_CREAT);
497 UCX_TEST_ASSERT(f1, "f3 not opened");
498
499 // test unlink
500 err = vfs_unlink(vfs, "/file1");
501 UCX_TEST_ASSERT(err == 0, "unlink /file1 failed");
502 err = vfs_unlink(vfs, "/dir1/file3");
503 UCX_TEST_ASSERT(err == 0, "unlink /dir1/file3 failed");
504
505 err = vfs_unlink(vfs, "/filex");
506 UCX_TEST_ASSERT(err != 0, "unlink /filex should fail");
507
508 // check if files were removed
509 SYS_FILE o1 = vfs_open(vfs, "/file1", O_RDONLY);
510 UCX_TEST_ASSERT(o1 == NULL, "/file1 not deleted");
511 SYS_FILE o3 = vfs_open(vfs, "/dir1/file3", O_RDONLY);
512 UCX_TEST_ASSERT(o1 == NULL, "/dir1/file3 not deleted");
513
514 // file2 should still be there
515 SYS_FILE o2 = vfs_open(vfs, "/file2", O_RDONLY);
516 UCX_TEST_ASSERT(o2, "/file2 deleted");
517
518 // check if dir unlink fails
519 err = vfs_unlink(vfs, "/dir1");
520 UCX_TEST_ASSERT(err != 0, "unlink dir1 should fail");
521
522 UCX_TEST_END;
523
524 testutil_destroy_session(sn);
525 }
526
527 UCX_TEST(test_vfs_rmdir) {
528 Session *sn = testutil_session();
529 Request *rq = testutil_request(sn->pool, "PUT", "/");
530 rq->vfs = testvfs_create(sn);
531
532 VFSContext *vfs = vfs_request_context(sn, rq);
533
534 UCX_TEST_BEGIN;
535 // prepare test
536 int err;
537 err = vfs_mkdir(vfs, "/dir1");
538 UCX_TEST_ASSERT(err == 0, "mkdir 1: error not 0");
539 err = vfs_mkdir(vfs, "/dir2");
540 UCX_TEST_ASSERT(err == 0, "mkdir 1: error not 0");
541
542 SYS_FILE f1 = vfs_open(vfs, "/dir1/file1", O_CREAT);
543 UCX_TEST_ASSERT(f1, "f1 not opened");
544
545 err = vfs_rmdir(vfs, "/dir1");
546 UCX_TEST_ASSERT(err != 0, "rmdir /dir1 should fail");
547 err = vfs_rmdir(vfs, "/dir2");
548 UCX_TEST_ASSERT(err == 0, "rmdir /dir2 failed");
549
550 err = vfs_unlink(vfs, "/dir1/file1");
551 UCX_TEST_ASSERT(err == 0, "unlink failed");
552 err = vfs_rmdir(vfs, "/dir1");
553 UCX_TEST_ASSERT(err == 0, "rmdir /dir1 (2) failed");
554
555 UCX_TEST_END;
556
557 testutil_destroy_session(sn);
558 }

mercurial