b067f53684c32d02a63aba00b531485888264019
[yaffs2.git] / rtems / rtems_yaffs.c
1 /*
2  * YAFFS port to RTEMS
3  *
4  * Copyright (C) 2010, 2011 Sebastien Bourdeauducq
5  * Copyright (C) 2011 Stephan Hoffmann <sho@reLinux.de>
6  * Copyright (C) 2011-2012 embedded brains GmbH <rtems@embedded-brains.de>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License version 2 as
10  * published by the Free Software Foundation.
11  *
12  * As a special exception, linking other files with the object code from
13  * this one to produce an executable application does not by itself cause
14  * the resulting executable application to be covered by the GNU General
15  * Public License.
16  * This exception does not however invalidate any other reasons why the
17  * executable file might be covered by the GNU Public License. In particular,
18  * the other YAFFS files are not covered by this exception, and using them
19  * in a proprietary application requires a paid license from Aleph One.
20  */
21
22 #include <rtems.h>
23 #include <rtems/libio_.h>
24 #include <rtems/seterr.h>
25 #include <rtems/userenv.h>
26 #include <sys/stat.h>
27 #include <errno.h>
28 #include <limits.h>
29 #include <stdlib.h>
30 #include <dirent.h>
31
32 #include "yportenv.h"
33
34 #include "yaffs_guts.h"
35 #include "yaffs_trace.h"
36 #include "yaffs_packedtags2.h"
37
38 #include "rtems_yaffs.h"
39
40 /* RTEMS interface */
41
42 static const rtems_filesystem_file_handlers_r yaffs_directory_handlers;
43 static const rtems_filesystem_file_handlers_r yaffs_file_handlers;
44 static const rtems_filesystem_operations_table yaffs_ops;
45
46 /* locking */
47
48 static void ylock(struct yaffs_dev *dev)
49 {
50         rtems_yaffs_os_context *os_context = dev->os_context;
51         (*os_context->lock)(dev, os_context);
52 }
53
54 static void yunlock(struct yaffs_dev *dev)
55 {
56         rtems_yaffs_os_context *os_context = dev->os_context;
57         (*os_context->unlock)(dev, os_context);
58 }
59
60 static void rtems_yaffs_os_unmount(struct yaffs_dev *dev)
61 {
62         rtems_yaffs_os_context *os_context = dev->os_context;
63         (*os_context->unmount)(dev, os_context);
64 }
65
66 static struct yaffs_obj *ryfs_get_object_by_location(
67         const rtems_filesystem_location_info_t *loc
68 )
69 {
70         return loc->node_access;
71 }
72
73 static struct yaffs_obj *ryfs_get_object_by_iop(
74         const rtems_libio_t *iop
75 )
76 {
77         return iop->pathinfo.node_access;
78 }
79
80 static struct yaffs_dev *ryfs_get_device_by_mt_entry(
81         const rtems_filesystem_mount_table_entry_t *mt_entry
82 )
83 {
84         return mt_entry->fs_info;
85 }
86
87 static void ryfs_set_location(rtems_filesystem_location_info_t *loc, struct yaffs_obj *obj)
88 {
89         loc->node_access = obj;
90
91         switch (obj->variant_type) {
92                 case YAFFS_OBJECT_TYPE_FILE:
93                         loc->handlers = &yaffs_file_handlers;
94                         break;
95                 case YAFFS_OBJECT_TYPE_DIRECTORY:
96                         loc->handlers = &yaffs_directory_handlers;
97                         break;
98                 default:
99                         loc->handlers = &rtems_filesystem_handlers_default;
100                         break;
101         };
102 }
103
104 static bool ryfs_eval_is_directory(
105         rtems_filesystem_eval_path_context_t *ctx,
106         void *arg
107 )
108 {
109         rtems_filesystem_location_info_t *currentloc =
110                 rtems_filesystem_eval_path_get_currentloc(ctx);
111         struct yaffs_obj *obj = ryfs_get_object_by_location(currentloc);
112
113         obj = yaffs_get_equivalent_obj(obj);
114
115         return obj->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY;
116 }
117
118 static const char *ryfs_make_string(char *buf, const char *src, size_t len)
119 {
120         buf [len] = '\0';
121
122         return memcpy(buf, src, len);
123 }
124
125 static struct yaffs_obj *ryfs_search_in_directory(
126         struct yaffs_obj *dir,
127         const char *token,
128         size_t tokenlen
129 )
130 {
131         if (rtems_filesystem_is_parent_directory(token, tokenlen)) {
132                 dir = dir->parent;
133         } else if (!rtems_filesystem_is_current_directory(token, tokenlen)) {
134                 if (tokenlen < YAFFS_MAX_NAME_LENGTH) {
135                         char buf [YAFFS_MAX_NAME_LENGTH + 1];
136
137                         dir = yaffs_find_by_name(
138                                 dir,
139                                 ryfs_make_string(buf, token, tokenlen)
140                         );
141                 } else {
142                         dir = NULL;
143                 }
144         }
145
146         return dir;
147 }
148
149 static rtems_filesystem_eval_path_generic_status ryfs_eval_token(
150         rtems_filesystem_eval_path_context_t *ctx,
151         void *arg,
152         const char *token,
153         size_t tokenlen
154 )
155 {
156         rtems_filesystem_eval_path_generic_status status =
157                 RTEMS_FILESYSTEM_EVAL_PATH_GENERIC_DONE;
158         rtems_filesystem_location_info_t *currentloc =
159                 rtems_filesystem_eval_path_get_currentloc(ctx);
160         struct yaffs_obj *dir = ryfs_get_object_by_location(currentloc);
161         bool access_ok = rtems_filesystem_eval_path_check_access(
162                 ctx,
163                 RTEMS_FS_PERMS_EXEC,
164                 dir->yst_mode,
165                 (uid_t) dir->yst_uid,
166                 (gid_t) dir->yst_gid
167         );
168
169         if (access_ok) {
170                 struct yaffs_obj *entry = ryfs_search_in_directory(dir, token, tokenlen);
171
172                 if (entry != NULL) {
173                         bool terminal = !rtems_filesystem_eval_path_has_path(ctx);
174                         int eval_flags = rtems_filesystem_eval_path_get_flags(ctx);
175                         bool follow_hard_link = (eval_flags & RTEMS_FS_FOLLOW_HARD_LINK) != 0;
176                         bool follow_sym_link = (eval_flags & RTEMS_FS_FOLLOW_SYM_LINK) != 0;
177                         enum yaffs_obj_type type = entry->variant_type;
178
179                         rtems_filesystem_eval_path_clear_token(ctx);
180
181                         if (type == YAFFS_OBJECT_TYPE_HARDLINK && (follow_hard_link || !terminal)) {
182                                 entry = yaffs_get_equivalent_obj(entry);
183                         }
184
185                         if (type == YAFFS_OBJECT_TYPE_SYMLINK && (follow_sym_link || !terminal)) {
186                                 const char *target = entry->variant.symlink_variant.alias;
187
188                                 rtems_filesystem_eval_path_recursive(ctx, target, strlen(target));
189                         } else {
190                                 ryfs_set_location(currentloc, entry);
191
192                                 if (!terminal) {
193                                         status = RTEMS_FILESYSTEM_EVAL_PATH_GENERIC_CONTINUE;
194                                 }
195                         }
196                 } else {
197                         status = RTEMS_FILESYSTEM_EVAL_PATH_GENERIC_NO_ENTRY;
198                 }
199         }
200
201         return status;
202 }
203
204 static const rtems_filesystem_eval_path_generic_config ryfs_eval_config = {
205         .is_directory = ryfs_eval_is_directory,
206         .eval_token = ryfs_eval_token
207 };
208
209 static void ryfs_eval_path(rtems_filesystem_eval_path_context_t *ctx)
210 {
211         rtems_filesystem_eval_path_generic(ctx, NULL, &ryfs_eval_config);
212 }
213
214 /* Helper functions */
215
216 static int ryfs_mknod(
217         const rtems_filesystem_location_info_t *parentloc,
218         const char *name,
219         size_t namelen,
220         mode_t mode,
221         dev_t dev
222 )
223 {
224         int rv = 0;
225         struct yaffs_obj *parent = ryfs_get_object_by_location(parentloc);
226         struct yaffs_obj *(*create)(
227                 struct yaffs_obj *parent,
228                 const YCHAR *name,
229                 u32 mode,
230                 u32 uid,
231                 u32 gid
232         );
233
234         switch (mode & S_IFMT) {
235                 case S_IFREG:
236                         create = yaffs_create_file;
237                         break;
238                 case S_IFDIR:
239                         create = yaffs_create_dir;
240                         break;
241                 default:
242                         errno = EINVAL;
243                         rv = -1;
244                         break;
245         }
246
247         if (rv == 0) {
248                 char buf [YAFFS_MAX_NAME_LENGTH + 1];
249                 struct yaffs_obj *entry = (*create)(
250                         parent,
251                         ryfs_make_string(buf, name, namelen),
252                         mode,
253                         geteuid(),
254                         getegid()
255                 );
256
257                 if (entry == NULL) {
258                         errno = ENOSPC;
259                         rv = -1;
260                 }
261         }
262
263         return rv;
264 }
265
266 static int ryfs_utime(
267         const rtems_filesystem_location_info_t *loc,
268         time_t actime,
269         time_t modtime
270 )
271 {
272         int rv = 0;
273         struct yaffs_obj *obj = ryfs_get_object_by_location(loc);
274
275         obj = yaffs_get_equivalent_obj(obj);
276         if (obj != NULL) {
277                 obj->dirty = 1;
278                 obj->yst_atime = (u32) actime;
279                 obj->yst_mtime = (u32) modtime;
280                 obj->yst_ctime = (u32) time(NULL);
281         } else {
282                 errno = EIO;
283                 rv = -1;
284         }
285
286         return rv;
287 }
288
289 static int ryfs_rename(
290         const rtems_filesystem_location_info_t *old_parent_loc,
291         const rtems_filesystem_location_info_t *old_loc,
292         const rtems_filesystem_location_info_t *new_parent_loc,
293         const char *name,
294         size_t namelen
295 )
296 {
297         int rv = 0;
298         struct yaffs_obj *obj = ryfs_get_object_by_location(old_loc);
299         char old_name_buf [YAFFS_MAX_NAME_LENGTH + 1];
300         char new_name_buf [YAFFS_MAX_NAME_LENGTH + 1];
301         int yc;
302
303         yaffs_get_obj_name(obj, old_name_buf, sizeof(old_name_buf));
304         yc = yaffs_rename_obj(
305                 obj->parent,
306                 old_name_buf,
307                 ryfs_get_object_by_location(new_parent_loc),
308                 ryfs_make_string(new_name_buf, name, namelen)
309         );
310         if (yc != YAFFS_OK) {
311                 errno = EIO;
312                 rv = -1;
313         }
314
315         return rv;
316 }
317
318 static ssize_t ryfs_dir_read(rtems_libio_t *iop, void *buffer, size_t count)
319 {
320         struct yaffs_obj *obj;
321         struct yaffs_dev *dev;
322         struct dirent *de = (struct dirent *)buffer;
323         size_t i;
324         size_t maxcount;
325         struct list_head *next;
326         ssize_t readlen;
327
328         obj = (struct yaffs_obj *)iop->pathinfo.node_access;
329         dev = obj->my_dev;
330         maxcount = count / sizeof(struct dirent);
331
332         ylock(dev);
333
334         if(iop->offset == 0) {
335                 if(list_empty(&obj->variant.dir_variant.children))
336                         iop->data1 = NULL;
337                 else
338                         iop->data1 = list_entry(obj->variant.dir_variant.children.next, struct yaffs_obj, siblings);
339         }
340
341         i = 0;
342         while((i < maxcount) && (iop->data1 != NULL)) {
343                 de[i].d_ino = (long)yaffs_get_equivalent_obj((struct yaffs_obj *)iop->data1)->obj_id;
344                 de[i].d_off = 0;
345                 yaffs_get_obj_name((struct yaffs_obj *)iop->data1, de[i].d_name, NAME_MAX);
346                 de[i].d_reclen = sizeof(struct dirent);
347                 de[i].d_namlen = (unsigned short)strnlen(de[i].d_name, NAME_MAX);
348
349                 i++;
350                 next = ((struct yaffs_obj *)iop->data1)->siblings.next;
351                 if(next == &obj->variant.dir_variant.children)
352                         iop->data1 = NULL; /* end of list */
353                 else
354                         iop->data1 = list_entry(next, struct yaffs_obj, siblings);
355         }
356
357         readlen = (ssize_t)(i * sizeof(struct dirent));
358         iop->offset = iop->offset + readlen;
359
360         yunlock(dev);
361
362         return readlen;
363 }
364
365 static int ryfs_fstat(const rtems_filesystem_location_info_t *loc, struct stat *buf)
366 {
367         int rv = 0;
368         struct yaffs_obj *obj = ryfs_get_object_by_location(loc);
369         struct yaffs_dev *dev = obj->my_dev;
370         rtems_yaffs_os_context *os_context = dev->os_context;
371
372         ylock(dev);
373
374         obj = yaffs_get_equivalent_obj(obj);
375         if (obj != NULL) {
376                 buf->st_dev = os_context->dev;
377                 buf->st_ino = obj->obj_id;
378                 buf->st_mode = obj->yst_mode;
379                 buf->st_nlink = (nlink_t) yaffs_get_obj_link_count(obj);
380                 buf->st_rdev = obj->yst_rdev;
381                 buf->st_size = yaffs_get_obj_length(obj);
382                 buf->st_blksize = obj->my_dev->data_bytes_per_chunk;
383                 buf->st_blocks = (blkcnt_t)
384                         ((buf->st_size + buf->st_blksize - 1) / buf->st_blksize);
385                 buf->st_uid = (uid_t) obj->yst_uid;
386                 buf->st_gid = (gid_t) obj->yst_gid;
387                 buf->st_atime = (time_t) obj->yst_atime;
388                 buf->st_ctime = (time_t) obj->yst_ctime;
389                 buf->st_mtime = (time_t) obj->yst_mtime;
390         } else {
391                 errno = EIO;
392                 rv = -1;
393         }
394
395         yunlock(dev);
396
397         return rv;
398 }
399
400 static int ryfs_fchmod(const rtems_filesystem_location_info_t *loc, mode_t mode)
401 {
402         int rv = 0;
403         struct yaffs_obj *obj = ryfs_get_object_by_location(loc);
404         int yc;
405
406         obj = yaffs_get_equivalent_obj(obj);
407         if (obj != NULL) {
408                 obj->yst_mode = mode;
409                 obj->dirty = 1;
410                 yc = yaffs_flush_file(obj, 0, 0);
411         } else {
412                 yc = YAFFS_FAIL;
413         }
414
415         if (yc != YAFFS_OK) {
416                 errno = EIO;
417                 rv = -1;
418         }
419
420         return rv;
421 }
422
423 static int ryfs_chown(
424         const rtems_filesystem_location_info_t *loc,
425         uid_t owner,
426         gid_t group
427 )
428 {
429         int rv = 0;
430         struct yaffs_obj *obj = ryfs_get_object_by_location(loc);
431         int yc;
432
433         obj = yaffs_get_equivalent_obj(obj);
434         if (obj != NULL) {
435                 obj->yst_uid = owner;
436                 obj->yst_gid = group;
437                 obj->dirty = 1;
438                 yc = yaffs_flush_file(obj, 0, 0);
439         } else {
440                 yc = YAFFS_FAIL;
441         }
442
443         if (yc != YAFFS_OK) {
444                 errno = EIO;
445                 rv = -1;
446         }
447
448         return rv;
449 }
450
451 static int ryfs_fsync_or_fdatasync(rtems_libio_t *iop)
452 {
453         int rv = 0;
454         struct yaffs_obj *obj = ryfs_get_object_by_iop(iop);
455         struct yaffs_dev *dev = obj->my_dev;
456         int yc;
457
458         ylock(dev);
459         yc = yaffs_flush_file(obj, 0, 1);
460         if (rtems_filesystem_location_is_instance_root(&iop->pathinfo)) {
461                 yaffs_flush_whole_cache(dev);
462         }
463         yunlock(dev);
464
465         if (yc != YAFFS_OK) {
466                 errno = EIO;
467                 rv = -1;
468         }
469
470         return rv;
471 }
472
473 static int ryfs_rmnod(
474         const rtems_filesystem_location_info_t *parentloc,
475         const rtems_filesystem_location_info_t *loc
476 )
477 {
478         int rv = 0;
479         struct yaffs_obj *obj = ryfs_get_object_by_location(loc);
480         int yc = yaffs_del_obj(obj);
481
482         if (yc != YAFFS_OK) {
483                 errno = ENOTEMPTY;
484                 rv = -1;
485         }
486
487         return rv;
488 }
489
490 static int ryfs_file_open(rtems_libio_t *iop, const char *pathname, int oflag, mode_t mode)
491 {
492         struct yaffs_obj *obj = ryfs_get_object_by_iop(iop);
493         struct yaffs_dev *dev = obj->my_dev;
494         int length = 0;
495
496         ylock(dev);
497         length = yaffs_get_obj_length(obj);
498         if ((iop->flags & LIBIO_FLAGS_APPEND) != 0) {
499                 iop->offset = length;
500         }
501         yunlock(dev);
502
503         return 0;
504 }
505
506 static int ryfs_file_close(rtems_libio_t *iop)
507 {
508         struct yaffs_obj *obj = ryfs_get_object_by_iop(iop);
509         struct yaffs_dev *dev = obj->my_dev;
510
511         ylock(dev);
512         yaffs_flush_file(obj, 1, 0);
513         yunlock(dev);
514
515         return 0;
516 }
517
518 static ssize_t ryfs_file_read(rtems_libio_t *iop, void *buffer, size_t count)
519 {
520         struct yaffs_obj *obj = ryfs_get_object_by_iop(iop);
521         struct yaffs_dev *dev = obj->my_dev;
522         ssize_t nr;
523         int ol;
524         size_t maxread;
525
526         ylock(dev);
527
528         ol = yaffs_get_obj_length(obj);
529         if(iop->offset >= ol)
530                 maxread = 0;
531         else
532                 maxread = (size_t)(ol - (int)iop->offset);
533         if(count > maxread)
534                 count = maxread;
535
536         nr = yaffs_file_rd(obj, buffer, iop->offset, (int)count);
537         if (nr >= 0) {
538                 iop->offset += nr;
539         } else {
540                 errno = EIO;
541                 nr = -1;
542         }
543
544         yunlock(dev);
545
546         return nr;
547 }
548
549 static ssize_t ryfs_file_write(rtems_libio_t *iop, const void *buffer, size_t count)
550 {
551         struct yaffs_obj *obj = ryfs_get_object_by_iop(iop);
552         struct yaffs_dev *dev = obj->my_dev;
553         ssize_t rv = -1;
554         int max_size = INT_MAX;
555         off_t offset;
556
557         if (count == 0) {
558                 return 0;
559         }
560
561         ylock(dev);
562         offset = iop->offset;
563         if (offset < max_size) {
564                 size_t max_count = max_size - (size_t) offset;
565
566                 if (count > max_count) {
567                         count = max_count;
568                 }
569
570                 rv = yaffs_wr_file(obj, buffer, offset, (int) count, 0);
571                 if (rv > 0) {
572                         iop->offset += rv;
573                 } else {
574                         errno = ENOSPC;
575                         rv = -1;
576                 }
577         } else {
578                 errno = EFBIG;
579         }
580         yunlock(dev);
581
582         return rv;
583 }
584
585 static int ryfs_file_ftruncate(rtems_libio_t *iop, off_t length)
586 {
587         int rv = 0;
588         struct yaffs_obj *obj = ryfs_get_object_by_iop(iop);
589         struct yaffs_dev *dev = obj->my_dev;
590         int yc;
591
592         ylock(dev);
593         yc = yaffs_resize_file(obj, length);
594         yunlock(dev);
595
596         if (yc != YAFFS_OK) {
597                 errno = EIO;
598                 rv = -1;
599         }
600
601         return rv;
602 }
603
604 int rtems_yaffs_mount_handler(rtems_filesystem_mount_table_entry_t *mt_entry, const void *data)
605 {
606         const rtems_yaffs_mount_data *mount_data = data;
607         struct yaffs_dev *dev = mount_data->dev;
608
609         if (dev->read_only && mt_entry->writeable) {
610                 errno = EACCES;
611                 return -1;
612         }
613
614         ylock(dev);
615         if (yaffs_guts_initialise(dev) == YAFFS_FAIL) {
616                 yunlock(dev);
617                 errno = ENOMEM;
618                 return -1;
619         }
620
621         mt_entry->fs_info = dev;
622         mt_entry->ops = &yaffs_ops;
623         mt_entry->mt_fs_root->location.node_access = dev->root_dir;
624         mt_entry->mt_fs_root->location.handlers = &yaffs_directory_handlers;
625
626         yaffs_flush_whole_cache(dev);
627         yunlock(dev);
628
629         return 0;
630 }
631
632 static void ryfs_fsunmount(rtems_filesystem_mount_table_entry_t *mt_entry)
633 {
634         struct yaffs_dev *dev = ryfs_get_device_by_mt_entry(mt_entry);
635
636         ylock(dev);
637         yaffs_flush_whole_cache(dev);
638         yaffs_deinitialise(dev);
639         yunlock(dev);
640         rtems_yaffs_os_unmount(dev);
641 }
642
643 static void ryfs_lock(const rtems_filesystem_mount_table_entry_t *mt_entry)
644 {
645         struct yaffs_dev *dev = ryfs_get_device_by_mt_entry(mt_entry);
646
647         ylock(dev);
648 }
649
650 static void ryfs_unlock(const rtems_filesystem_mount_table_entry_t *mt_entry)
651 {
652         struct yaffs_dev *dev = ryfs_get_device_by_mt_entry(mt_entry);
653
654         yunlock(dev);
655 }
656
657 static const rtems_filesystem_file_handlers_r yaffs_directory_handlers = {
658         .open_h = rtems_filesystem_default_open,
659         .close_h = rtems_filesystem_default_close,
660         .read_h = ryfs_dir_read,
661         .write_h = rtems_filesystem_default_write,
662         .ioctl_h = rtems_filesystem_default_ioctl,
663         .lseek_h = rtems_filesystem_default_lseek_directory,
664         .fstat_h = ryfs_fstat,
665         .ftruncate_h = rtems_filesystem_default_ftruncate_directory,
666         .fsync_h = ryfs_fsync_or_fdatasync,
667         .fdatasync_h = ryfs_fsync_or_fdatasync,
668         .fcntl_h = rtems_filesystem_default_fcntl
669 };
670
671 static const rtems_filesystem_file_handlers_r yaffs_file_handlers = {
672         .open_h = ryfs_file_open,
673         .close_h = ryfs_file_close,
674         .read_h = ryfs_file_read,
675         .write_h = ryfs_file_write,
676         .ioctl_h = rtems_filesystem_default_ioctl,
677         .lseek_h = rtems_filesystem_default_lseek_file,
678         .fstat_h = ryfs_fstat,
679         .ftruncate_h = ryfs_file_ftruncate,
680         .fsync_h = ryfs_fsync_or_fdatasync,
681         .fdatasync_h = ryfs_fsync_or_fdatasync,
682         .fcntl_h = rtems_filesystem_default_fcntl
683 };
684
685 static const rtems_filesystem_operations_table yaffs_ops = {
686         .lock_h = ryfs_lock,
687         .unlock_h = ryfs_unlock,
688         .eval_path_h = ryfs_eval_path,
689         .link_h = rtems_filesystem_default_link,
690         .are_nodes_equal_h = rtems_filesystem_default_are_nodes_equal,
691         .mknod_h = ryfs_mknod,
692         .rmnod_h = ryfs_rmnod,
693         .fchmod_h = ryfs_fchmod,
694         .chown_h = ryfs_chown,
695         .clonenod_h = rtems_filesystem_default_clonenode,
696         .freenod_h = rtems_filesystem_default_freenode,
697         .mount_h = rtems_filesystem_default_mount,
698         .unmount_h = rtems_filesystem_default_unmount,
699         .fsunmount_me_h = ryfs_fsunmount,
700         .utime_h = ryfs_utime,
701         .symlink_h = rtems_filesystem_default_symlink,
702         .readlink_h = rtems_filesystem_default_readlink,
703         .rename_h = ryfs_rename,
704         .statvfs_h = rtems_filesystem_default_statvfs
705 };