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