1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <errno.h>
32
33 #include <cx/string.h>
34 #include <cx/list.h>
35 #include <cx/map.h>
36
37 #include "../daemon/session.h"
38
39 #include "testutils.h"
40
41 #include "vfs.h"
42
43 typedef struct TestVFS {
44 CxMap *files;
45 int count_unlink;
46 int count_rmdir;
47 } TestVFS;
48
49 typedef struct TestVFSFile {
50 VFSFile file;
51 cxmutstr path;
52 int isdir;
53 CxBuffer content;
54 } TestVFSFile;
55
56 typedef struct TestVFSDir {
57 VFSDir dir;
58 TestVFSFile *file;
59 CxIterator i;
60 cxmutstr name;
61 } TestVFSDir;
62
63
64
65 static const char* test_resource_name(
char *url) {
66 cxstring urlstr = cx_str(url);
67 if(urlstr.ptr[urlstr.length-
1] ==
'/') {
68 urlstr.length--;
69 }
70 cxstring resname = cx_strrchr(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 cxmutstr prefix = cx_strcat(
2, vfsdir->file->path, cx_str(
"/"));
83
84
85
86
87
88 TestVFSFile *file =
NULL;
89 cx_foreach(TestVFSFile *, entry, vfsdir->i) {
90 if(file)
break;
91 cxmutstr file_path = cx_strcat(
92 2,
93 prefix,
94 cx_str(test_resource_name(entry->path.ptr)));
95 file = cxMapGet(vfs->files, cx_hash_key(file_path.ptr, file_path.length));
96 free(file_path.ptr);
97 }
98 free(prefix.ptr);
99
100 if(file) {
101 vfsdir->name = cx_strdup_a(
102 pool_allocator(dir->ctx->sn->pool),
103 cx_str(test_resource_name(file->path.ptr)));
104 ZERO(entry,
sizeof(
VFS_ENTRY));
105 entry->name = vfsdir->name.ptr;
106
107 if(getstat) {
108 ZERO(&entry->stat,
sizeof(
struct stat));
109 if(file->isdir) {
110 entry->stat.st_mode =
S_IFDIR;
111 }
112 }
113
114 return 1;
115 }
else {
116 return 0;
117 }
118 }
119
120 void testvfs_dir_close(
VFS_DIR dir) {
121 TestVFSDir *testdir = (TestVFSDir*)dir;
122 pool_free(testdir->dir.ctx->sn->pool, dir);
123
124 }
125
126 ssize_t testvfs_read(
SYS_FILE fd,
void *buf,
size_t nbyte) {
127 TestVFSFile *file = (TestVFSFile*)fd;
128 return (
ssize_t)cxBufferRead(buf,
1, nbyte, &file->content);
129 }
130
131 ssize_t testvfs_write(
SYS_FILE fd,
const void *buf,
size_t nbyte) {
132 TestVFSFile *file = (TestVFSFile*)fd;
133 return (
ssize_t)cxBufferWrite(buf,
1, nbyte, &file->content);
134 }
135
136 ssize_t testvfs_pread(
SYS_FILE fd,
void *buf,
size_t nbyte,
off_t offset) {
137 TestVFSFile *file = (TestVFSFile*)fd;
138 file->content.pos = (
size_t)offset;
139 return testvfs_read(fd, buf, nbyte);
140 }
141
142 ssize_t testvfs_pwrite(
SYS_FILE fd,
const void *buf,
size_t nbyte,
off_t offset) {
143 TestVFSFile *file = (TestVFSFile*)fd;
144 file->content.pos = (
size_t)offset;
145 return testvfs_write(fd, buf, nbyte);
146 }
147
148 off_t testvfs_seek(
SYS_FILE fd,
off_t offset,
int whence) {
149 TestVFSFile *file = (TestVFSFile*)fd;
150 cxBufferSeek(&file->content, offset, whence);
151 return (
off_t)file->content.pos;
152 }
153
154 void testvfs_close(
SYS_FILE fd) {
155 TestVFSFile *file = (TestVFSFile*)fd;
156 file->content.pos =
0;
157 }
158
159 VFS_IO test_file_io = {
160 testvfs_read,
161 testvfs_write,
162 testvfs_pread,
163 testvfs_pwrite,
164 testvfs_seek,
165 testvfs_close,
166 NULL,
167 NULL
168 };
169
170 VFS_DIRIO test_dir_io = {
171 testvfs_readdir,
172 testvfs_dir_close
173 };
174
175
176
177
178 SYS_FILE testvfs_open(VFSContext *ctx,
const char *path,
int oflags) {
179 TestVFS *vfs = ctx->vfs->instance;
180 TestVFSFile *file =
NULL;
181
182 cxstring s_path = cx_str((
char*)path);
183 if(cx_strsuffix(s_path, cx_str(
"/"))) {
184 s_path.length--;
185 }
186
187 file = cxMapGet(vfs->files, cx_hash_key_bytes((
const unsigned char*)s_path.ptr, s_path.length));
188 if(!file) {
189 if((oflags &
O_CREAT) ==
O_CREAT) {
190 file = pool_malloc(ctx->sn->pool,
sizeof(TestVFSFile));
191 ZERO(file,
sizeof(TestVFSFile));
192 file->file.ctx = ctx;
193 file->path = cx_strdup_a(pool_allocator(ctx->sn->pool), s_path);
194 file->file.io = &test_file_io;
195
196 cxBufferInit(&file->content, pool_malloc(ctx->sn->pool,
2048),
2048, pool_allocator(ctx->sn->pool),
0);
197
198 cxMapPut(vfs->files, cx_hash_key((
void*)s_path.ptr, s_path.length), file);
199 }
else {
200 ctx->vfs_errno =
ENOENT;
201 }
202 }
203
204 return (
SYS_FILE)file;
205 }
206
207 int testvfs_stat(VFSContext *ctx,
const char *path,
struct stat *buf) {
208 TestVFS *vfs = ctx->vfs->instance;
209 TestVFSFile *file =
NULL;
210
211 cxstring s_path = cx_str((
char*)path);
212 if(cx_strsuffix(s_path, cx_str(
"/"))) {
213 s_path.length--;
214 }
215
216 file = cxMapGet(vfs->files, cx_hash_key((
void*)s_path.ptr, s_path.length));
217 if(!file) {
218 ctx->vfs_errno =
ENOENT;
219 return 1;
220 }
221
222 ZERO(buf,
sizeof(
struct stat));
223 if(file->isdir) {
224 buf->st_mode =
S_IFDIR;
225 }
226
227 return 0;
228 }
229
230 int testvfs_fstat(VFSContext *ctx,
SYS_FILE fd,
struct stat *buf) {
231 return 0;
232 }
233
234 VFS_DIR testvfs_opendir(VFSContext *ctx,
const char *path) {
235 TestVFS *vfs = ctx->vfs->instance;
236 TestVFSFile *file =
NULL;
237
238 cxstring s_path = cx_str((
char*)path);
239 if(cx_strsuffix(s_path, cx_str(
"/"))) {
240 s_path.length--;
241 }
242
243 file = cxMapGet(vfs->files, cx_hash_key((
void*)s_path.ptr, s_path.length));
244 if(!file) {
245 ctx->vfs_errno =
ENOENT;
246 return NULL;
247 }
248
249 if(!file->isdir) {
250 return NULL;
251 }
252
253 TestVFSDir *dir = pool_malloc(ctx->sn->pool,
sizeof(TestVFSDir));
254 ZERO(dir,
sizeof(TestVFSDir));
255 dir->file = file;
256 dir->i = cxMapIteratorValues(vfs->files);
257
258 dir->dir.ctx = ctx;
259 dir->dir.io = &test_dir_io;
260
261 return (
VFS_DIR)dir;
262 }
263
264 VFS_DIR testvfs_fdopendir(VFSContext *ctx,
SYS_FILE fd) {
265 TestVFS *vfs = ctx->vfs->instance;
266 TestVFSFile *file = (TestVFSFile*)fd;
267 if(!file->isdir) {
268 return NULL;
269 }
270
271 TestVFSDir *dir = pool_malloc(ctx->sn->pool,
sizeof(TestVFSDir));
272 ZERO(dir,
sizeof(TestVFSDir));
273 dir->file = file;
274 dir->i = cxMapIteratorValues(vfs->files);
275
276 dir->dir.ctx = ctx;
277 dir->dir.io = &test_dir_io;
278
279 return (
VFS_DIR)dir;
280 }
281
282 int testvfs_mkdir(VFSContext *ctx,
const char *path) {
283 SYS_FILE fd = testvfs_open(ctx, path,
O_CREAT);
284 if(!fd) {
285 return 1;
286 }
287
288 TestVFSFile *file = (TestVFSFile*)fd;
289 file->isdir =
1;
290
291 return 0;
292 }
293
294 int testvfs_unlink(VFSContext *ctx,
const char *path) {
295 TestVFS *vfs = ctx->vfs->instance;
296 CxHashKey path_key = cx_hash_key_str(path);
297 TestVFSFile *file = cxMapGet(vfs->files, path_key);
298 if(!file) {
299 return 1;
300 }
301
302 if(file->isdir) {
303 return 1;
304 }
305
306 cxMapRemove(vfs->files, path_key);
307 vfs->count_unlink++;
308 return 0;
309 }
310
311 int testvfs_rmdir(VFSContext *ctx,
const char *path) {
312 TestVFS *vfs = ctx->vfs->instance;
313 CxHashKey path_key = cx_hash_key_str(path);
314 TestVFSFile *dir = cxMapGet(vfs->files, path_key);
315 if(!dir) {
316 ctx->vfs_errno =
ENOENT;
317 return 1;
318 }
319
320 if(!dir->isdir) {
321 return 1;
322 }
323
324 CxIterator i = cxMapIteratorValues(vfs->files);
325 cx_foreach(TestVFSFile *, f, i) {
326 if(f->path.length > dir->path.length && cx_strprefix(cx_strcast(f->path), cx_strcast(dir->path))){
327 return 1;
328 }
329 }
330
331 cxMapRemove(vfs->files, path_key);
332 vfs->count_rmdir++;
333 return 0;
334 }
335
336 static VFS testVFSClass = {
337 testvfs_open,
338 testvfs_stat,
339 testvfs_fstat,
340 testvfs_opendir,
341 testvfs_fdopendir,
342 testvfs_mkdir,
343 testvfs_unlink,
344 testvfs_rmdir,
345 0,
346 NULL
347 };
348
349
350 VFS* testvfs_create(Session *sn) {
351 TestVFS *vfs = pool_malloc(sn->pool,
sizeof(TestVFS));
352 vfs->count_unlink =
0;
353 vfs->count_rmdir =
0;
354 vfs->files = cxHashMapCreate(pool_allocator(sn->pool),
CX_STORE_POINTERS,
64);
355
356 testVFSClass.instance = vfs;
357 return &testVFSClass;
358 }
359
360
361
362
363
364
365
366
367 UCX_TEST(test_vfs_open) {
368 Session *sn = testutil_session();
369 Request *rq = testutil_request(sn->pool,
"PUT",
"/");
370 rq->vfs = testvfs_create(sn);
371
372 VFSContext *vfs = vfs_request_context(sn, rq);
373
374 UCX_TEST_BEGIN;
375
376 UCX_TEST_ASSERT(vfs,
"vfs is NULL");
377
378 SYS_FILE f1 = vfs_open(vfs,
"/file1",
O_CREAT);
379 UCX_TEST_ASSERT(f1,
"f1 not opened");
380
381 SYS_FILE f2 = vfs_open(vfs,
"/file1",
0);
382 UCX_TEST_ASSERT(f2,
"f2 not opened");
383
384 UCX_TEST_END;
385
386 testutil_destroy_session(sn);
387 }
388
389 UCX_TEST(test_vfs_mkdir) {
390 Session *sn = testutil_session();
391 Request *rq = testutil_request(sn->pool,
"PUT",
"/");
392 rq->vfs = testvfs_create(sn);
393
394 VFSContext *vfs = vfs_request_context(sn, rq);
395
396 UCX_TEST_BEGIN;
397
398 int err = vfs_mkdir(vfs,
"/dir");
399 UCX_TEST_ASSERT(err ==
0,
"error not 0");
400
401 SYS_FILE fd = vfs_open(vfs,
"/dir",
0);
402 UCX_TEST_ASSERT(fd,
"no fd");
403
404 UCX_TEST_END;
405
406 testutil_destroy_session(sn);
407 }
408
409 UCX_TEST(test_vfs_opendir) {
410 Session *sn = testutil_session();
411 Request *rq = testutil_request(sn->pool,
"PUT",
"/");
412 rq->vfs = testvfs_create(sn);
413
414 VFSContext *vfs = vfs_request_context(sn, rq);
415
416 UCX_TEST_BEGIN;
417
418 int err = vfs_mkdir(vfs,
"/dir");
419 UCX_TEST_ASSERT(err ==
0,
"error not 0");
420
421 VFSDir *dir = vfs_opendir(vfs,
"/dir");
422 UCX_TEST_ASSERT(dir,
"no dir");
423
424 UCX_TEST_END;
425
426 testutil_destroy_session(sn);
427 }
428
429 UCX_TEST(test_vfs_readdir) {
430 Session *sn = testutil_session();
431 Request *rq = testutil_request(sn->pool,
"PUT",
"/");
432 rq->vfs = testvfs_create(sn);
433
434 VFSContext *vfs = vfs_request_context(sn, rq);
435
436 UCX_TEST_BEGIN;
437
438 int err = vfs_mkdir(vfs,
"/dir");
439 UCX_TEST_ASSERT(err ==
0,
"error not 0");
440
441
442 UCX_TEST_ASSERT(vfs_open(vfs,
"/dir/file1",
O_CREAT),
"creation of file1 failed");
443 UCX_TEST_ASSERT(vfs_open(vfs,
"/dir/file2",
O_CREAT),
"creation of file2 failed");
444 UCX_TEST_ASSERT(vfs_open(vfs,
"/dir/file3",
O_CREAT),
"creation of file3 failed");
445 UCX_TEST_ASSERT(vfs_open(vfs,
"/dir/file4",
O_CREAT),
"creation of file4 failed");
446
447 VFSDir *dir = vfs_opendir(vfs,
"/dir");
448 UCX_TEST_ASSERT(dir,
"dir not opened");
449
450 CxMap *files = cxHashMapCreate(cxDefaultAllocator,
CX_STORE_POINTERS,
8);
451
452 VFSEntry entry;
453 while(vfs_readdir(dir, &entry)) {
454 cxMapPut(files, cx_hash_key_str(entry.name), dir);
455 }
456
457 UCX_TEST_ASSERT(files->size ==
4,
"wrong files count");
458 UCX_TEST_ASSERT(cxMapGet(files, cx_hash_key_str(
"file1")),
"file1 missing");
459 UCX_TEST_ASSERT(cxMapGet(files, cx_hash_key_str(
"file2")),
"file2 missing");
460 UCX_TEST_ASSERT(cxMapGet(files, cx_hash_key_str(
"file3")),
"file3 missing");
461 UCX_TEST_ASSERT(cxMapGet(files, cx_hash_key_str(
"file4")),
"file4 missing");
462
463 cxMapDestroy(files);
464
465 UCX_TEST_END;
466
467 testutil_destroy_session(sn);
468 }
469
470 UCX_TEST(test_vfs_unlink) {
471 Session *sn = testutil_session();
472 Request *rq = testutil_request(sn->pool,
"PUT",
"/");
473 rq->vfs = testvfs_create(sn);
474
475 VFSContext *vfs = vfs_request_context(sn, rq);
476
477 UCX_TEST_BEGIN;
478
479 int err;
480 err = vfs_mkdir(vfs,
"/dir1");
481 UCX_TEST_ASSERT(err ==
0,
"mkdir 1: error not 0");
482 err = vfs_mkdir(vfs,
"/dir2");
483 UCX_TEST_ASSERT(err ==
0,
"mkdir 1: error not 0");
484
485 SYS_FILE f1 = vfs_open(vfs,
"/file1",
O_CREAT);
486 UCX_TEST_ASSERT(f1,
"f1 not opened");
487
488 SYS_FILE f2 = vfs_open(vfs,
"/file2",
O_CREAT);
489 UCX_TEST_ASSERT(f1,
"f2 not opened");
490
491 SYS_FILE f3 = vfs_open(vfs,
"/dir1/file3",
O_CREAT);
492 UCX_TEST_ASSERT(f1,
"f3 not opened");
493
494
495 err = vfs_unlink(vfs,
"/file1");
496 UCX_TEST_ASSERT(err ==
0,
"unlink /file1 failed");
497 err = vfs_unlink(vfs,
"/dir1/file3");
498 UCX_TEST_ASSERT(err ==
0,
"unlink /dir1/file3 failed");
499
500 err = vfs_unlink(vfs,
"/filex");
501 UCX_TEST_ASSERT(err !=
0,
"unlink /filex should fail");
502
503
504 SYS_FILE o1 = vfs_open(vfs,
"/file1",
O_RDONLY);
505 UCX_TEST_ASSERT(o1 ==
NULL,
"/file1 not deleted");
506 SYS_FILE o3 = vfs_open(vfs,
"/dir1/file3",
O_RDONLY);
507 UCX_TEST_ASSERT(o1 ==
NULL,
"/dir1/file3 not deleted");
508
509
510 SYS_FILE o2 = vfs_open(vfs,
"/file2",
O_RDONLY);
511 UCX_TEST_ASSERT(o2,
"/file2 deleted");
512
513
514 err = vfs_unlink(vfs,
"/dir1");
515 UCX_TEST_ASSERT(err !=
0,
"unlink dir1 should fail");
516
517 UCX_TEST_END;
518
519 testutil_destroy_session(sn);
520 }
521
522 UCX_TEST(test_vfs_rmdir) {
523 Session *sn = testutil_session();
524 Request *rq = testutil_request(sn->pool,
"PUT",
"/");
525 rq->vfs = testvfs_create(sn);
526
527 VFSContext *vfs = vfs_request_context(sn, rq);
528
529 UCX_TEST_BEGIN;
530
531 int err;
532 err = vfs_mkdir(vfs,
"/dir1");
533 UCX_TEST_ASSERT(err ==
0,
"mkdir 1: error not 0");
534 err = vfs_mkdir(vfs,
"/dir2");
535 UCX_TEST_ASSERT(err ==
0,
"mkdir 1: error not 0");
536
537 SYS_FILE f1 = vfs_open(vfs,
"/dir1/file1",
O_CREAT);
538 UCX_TEST_ASSERT(f1,
"f1 not opened");
539
540 err = vfs_rmdir(vfs,
"/dir1");
541 UCX_TEST_ASSERT(err !=
0,
"rmdir /dir1 should fail");
542 err = vfs_rmdir(vfs,
"/dir2");
543 UCX_TEST_ASSERT(err ==
0,
"rmdir /dir2 failed");
544
545 err = vfs_unlink(vfs,
"/dir1/file1");
546 UCX_TEST_ASSERT(err ==
0,
"unlink failed");
547 err = vfs_rmdir(vfs,
"/dir1");
548 UCX_TEST_ASSERT(err ==
0,
"rmdir /dir1 (2) failed");
549
550 UCX_TEST_END;
551
552 testutil_destroy_session(sn);
553 }
554