1 /****************************************************************************
2  * fs/tmpfs/fs_tmpfs.c
3  *
4  * Licensed to the Apache Software Foundation (ASF) under one or more
5  * contributor license agreements.  See the NOTICE file distributed with
6  * this work for additional information regarding copyright ownership.  The
7  * ASF licenses this file to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance with the
9  * License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
15  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
16  * License for the specific language governing permissions and limitations
17  * under the License.
18  *
19  ****************************************************************************/
20 
21 /****************************************************************************
22  * Included Files
23  ****************************************************************************/
24 #include <string.h>
25 #include <fcntl.h>
26 #include <errno.h>
27 #include <assert.h>
28 #include <unistd.h>
29 #include <limits.h>
30 #include <sys/types.h>
31 #include <sys/time.h>
32 #include <linux/spinlock.h>
33 #include <sys/statfs.h>
34 #include "fs/dirent_fs.h"
35 #include "fs/mount.h"
36 #include "fs/file.h"
37 #include "fs/fs.h"
38 #include "los_tables.h"
39 #include "fs_tmpfs.h"
40 #include "los_vm_filemap.h"
41 #include "user_copy.h"
42 
43 #ifdef LOSCFG_FS_RAMFS
44 
45 /****************************************************************************
46  * Pre-processor Definitions
47  ****************************************************************************/
48 /*
49 #if CONFIG_FS_TMPFS_DIRECTORY_FREEGUARD <= CONFIG_FS_TMPFS_DIRECTORY_ALLOCGUARD
50 #  warning CONFIG_FS_TMPFS_DIRECTORY_FREEGUARD needs to be > ALLOCGUARD
51 #endif
52 
53 #if CONFIG_FS_TMPFS_FILE_FREEGUARD <= CONFIG_FS_TMPFS_FILE_ALLOCGUARD
54 #  warning CONFIG_FS_TMPFS_FILE_FREEGUARD needs to be > ALLOCGUARD
55 #endif
56 */
57 #define tmpfs_lock_file(tfo) \
58            (tmpfs_lock_object((struct tmpfs_object_s *)tfo))
59 #define tmpfs_lock_directory(tdo) \
60            (tmpfs_lock_object((struct tmpfs_object_s *)tdo))
61 #define tmpfs_unlock_file(tfo) \
62            (tmpfs_unlock_object((struct tmpfs_object_s *)tfo))
63 #define tmpfs_unlock_directory(tdo) \
64            (tmpfs_unlock_object((struct tmpfs_object_s *)tdo))
65 
66 /****************************************************************************
67  * Private Function Prototypes
68  ****************************************************************************/
69 
70 /* TMPFS helpers */
71 
72 static void tmpfs_lock_reentrant(struct tmpfs_sem_s *sem);
73 static void tmpfs_lock(struct tmpfs_s *fs);
74 static void tmpfs_unlock_reentrant(struct tmpfs_sem_s *sem);
75 static void tmpfs_unlock(struct tmpfs_s *fs);
76 static void tmpfs_lock_object(struct tmpfs_object_s *to);
77 static void tmpfs_unlock_object(struct tmpfs_object_s *to);
78 static void tmpfs_release_lockedobject(struct tmpfs_object_s *to);
79 static void tmpfs_release_lockedfile(struct tmpfs_file_s *tfo);
80 static struct tmpfs_dirent_s *tmpfs_find_dirent(struct tmpfs_directory_s *tdo,
81               const char *name);
82 static int  tmpfs_remove_dirent(struct tmpfs_directory_s *tdo,
83               struct tmpfs_object_s *to);
84 static int  tmpfs_add_dirent(struct tmpfs_directory_s **tdo,
85               struct tmpfs_object_s *to, const char *name);
86 static struct tmpfs_file_s *tmpfs_alloc_file(void);
87 static int tmpfs_create_file(struct tmpfs_s *fs,
88                              const char *relpath,
89                              struct tmpfs_directory_s *parent_input,
90                              struct tmpfs_file_s **tfo);
91 
92 static struct tmpfs_directory_s *tmpfs_alloc_directory(void);
93 static int tmpfs_create_directory(struct tmpfs_s *fs,
94                                   const char *relpath,
95                                   struct tmpfs_directory_s *parent,
96                                   struct tmpfs_directory_s **tdo);
97 
98 static int  tmpfs_find_object(struct tmpfs_s *fs,
99               const char *relpath, struct tmpfs_object_s **object,
100               struct tmpfs_directory_s **parent);
101 static int  tmpfs_find_file(struct tmpfs_s *fs,
102               const char *relpath,
103               struct tmpfs_file_s **tfo,
104               struct tmpfs_directory_s **parent);
105 static int  tmpfs_find_directory(struct tmpfs_s *fs,
106               const char *relpath,
107               struct tmpfs_directory_s **tdo,
108               struct tmpfs_directory_s **parent);
109 
110 /* File system operations */
111 
112 int tmpfs_close(struct file *filep);
113 off_t tmpfs_seek(struct file *filep, off_t offset, int whence);
114 int tmpfs_ioctl(struct file *filep, int cmd, unsigned long arg);
115 int tmpfs_sync(struct file *filep);
116 int tmpfs_closedir(struct Vnode *node, struct fs_dirent_s *dir);
117 int tmpfs_rewinddir(struct Vnode *vp, struct fs_dirent_s *dir);
118 int tmpfs_truncate(struct Vnode *vp, off_t len);
119 
120 int  tmpfs_mount(struct Mount *mnt, struct Vnode *device, const void *data);
121 int  tmpfs_unmount(struct Mount *mnt, struct Vnode **blkdriver);
122 
123 int tmpfs_lookup(struct Vnode *parent, const char *name, int len, struct Vnode **vpp);
124 ssize_t tmpfs_write(struct file *filep, const char *buffer, size_t buflen);
125 ssize_t tmpfs_read(struct file *filep, char *buffer, size_t buflen);
126 ssize_t tmpfs_readpage(struct Vnode *vnode, char *buffer, off_t off);
127 int tmpfs_stat(struct Vnode *vp, struct stat *st);
128 int tmpfs_opendir(struct Vnode *vp, struct fs_dirent_s *dir);
129 int tmpfs_readdir(struct Vnode *vp, struct fs_dirent_s *dir);
130 int tmpfs_rename(struct Vnode *oldVnode, struct Vnode *newParent, const char *oldname, const char *newname);
131 int tmpfs_mkdir(struct Vnode *parent, const char *relpath, mode_t mode, struct Vnode **vpp);
132 int tmpfs_create(struct Vnode *dvp, const char *path, int mode, struct Vnode **vpp);
133 int tmpfs_unlink(struct Vnode *parent, struct Vnode *node, const char *relpath);
134 int tmpfs_rmdir(struct Vnode *parent, struct Vnode *target, const char *dirname);
135 int tmpfs_reclaim(struct Vnode *vp);
136 
137 int tmpfs_statfs(struct Mount *mp, struct statfs *sbp);
138 
139 static void tmpfs_stat_common(struct tmpfs_object_s *to,
140                               struct stat *buf);
141 
142 /****************************************************************************
143  * Public Data
144  ****************************************************************************/
145 const struct MountOps tmpfs_operations = {
146     .Mount = tmpfs_mount,
147     .Unmount = tmpfs_unmount,
148     .Statfs = tmpfs_statfs,
149 };
150 
151 struct VnodeOps tmpfs_vops = {
152     .Lookup = tmpfs_lookup,
153     .Getattr = tmpfs_stat,
154     .Opendir = tmpfs_opendir,
155     .Readdir = tmpfs_readdir,
156     .ReadPage = tmpfs_readpage,
157     .WritePage = NULL,
158     .Rename = tmpfs_rename,
159     .Mkdir = tmpfs_mkdir,
160     .Create = tmpfs_create,
161     .Unlink =  tmpfs_unlink,
162     .Rmdir = tmpfs_rmdir,
163     .Reclaim = tmpfs_reclaim,
164     .Closedir = tmpfs_closedir,
165     .Close = NULL,
166     .Rewinddir = tmpfs_rewinddir,
167     .Truncate = tmpfs_truncate,
168 };
169 
170 struct file_operations_vfs tmpfs_fops = {
171     .seek = tmpfs_seek,
172     .write = tmpfs_write,
173     .read = tmpfs_read,
174     .ioctl = tmpfs_ioctl,
175     .mmap = OsVfsFileMmap,
176     .close = tmpfs_close,
177     .fsync = tmpfs_sync,
178 };
179 
180 static struct tmpfs_s tmpfs_superblock = {0};
181 
182 /****************************************************************************
183  * Name: tmpfs_timestamp
184  ****************************************************************************/
185 
tmpfs_timestamp(void)186 static time_t tmpfs_timestamp(void)
187 {
188   struct timeval tv;
189 
190   (void)gettimeofday(&tv, (struct timezone *)NULL);
191 
192   return (time_t)(tv.tv_sec);
193 }
194 
195 /****************************************************************************
196  * Name: tmpfs_lock_reentrant
197  ****************************************************************************/
198 
tmpfs_lock_reentrant(struct tmpfs_sem_s *sem)199 static void tmpfs_lock_reentrant(struct tmpfs_sem_s *sem)
200 {
201   pid_t me;
202 
203   /* Do we already hold the semaphore? */
204 
205   me = getpid();
206   if (me == sem->ts_holder)
207     {
208       /* Yes... just increment the count */
209 
210       sem->ts_count++;
211       DEBUGASSERT(sem->ts_count > 0);
212     }
213 
214   /* Take the semaphore (perhaps waiting) */
215 
216   else
217     {
218       while (sem_wait(&sem->ts_sem) != 0)
219         {
220           /* The only case that an error should occur here is if the wait
221            * was awakened by a signal.
222            */
223 
224           DEBUGASSERT(get_errno() == EINTR);
225         }
226 
227       /* No we hold the semaphore */
228 
229       sem->ts_holder = me;
230       sem->ts_count  = 1;
231     }
232 }
233 
234 /****************************************************************************
235  * Name: tmpfs_lock
236  ****************************************************************************/
237 
tmpfs_lock(struct tmpfs_s *fs)238 static void tmpfs_lock(struct tmpfs_s *fs)
239 {
240   tmpfs_lock_reentrant(&fs->tfs_exclsem);
241 }
242 
243 /****************************************************************************
244  * Name: tmpfs_lock_object
245  ****************************************************************************/
246 
tmpfs_lock_object(struct tmpfs_object_s *to)247 static void tmpfs_lock_object(struct tmpfs_object_s *to)
248 {
249   tmpfs_lock_reentrant(&to->to_exclsem);
250 }
251 
252 /****************************************************************************
253  * Name: tmpfs_unlock_reentrant
254  ****************************************************************************/
255 
tmpfs_unlock_reentrant(struct tmpfs_sem_s *sem)256 static void tmpfs_unlock_reentrant(struct tmpfs_sem_s *sem)
257 {
258   DEBUGASSERT(sem->ts_holder == getpid());
259 
260   /* Is this our last count on the semaphore? */
261 
262   if (sem->ts_count > 1)
263     {
264       /* No.. just decrement the count */
265 
266       sem->ts_count--;
267     }
268 
269   /* Yes.. then we can really release the semaphore */
270 
271   else
272     {
273       sem->ts_holder = TMPFS_NO_HOLDER;
274       sem->ts_count  = 0;
275       sem_post(&sem->ts_sem);
276     }
277 }
278 
279 /****************************************************************************
280  * Name: tmpfs_unlock
281  ****************************************************************************/
282 
tmpfs_unlock(struct tmpfs_s *fs)283 static void tmpfs_unlock(struct tmpfs_s *fs)
284 {
285   tmpfs_unlock_reentrant(&fs->tfs_exclsem);
286 }
287 
288 /****************************************************************************
289  * Name: tmpfs_unlock_object
290  ****************************************************************************/
291 
tmpfs_unlock_object(struct tmpfs_object_s *to)292 static void tmpfs_unlock_object(struct tmpfs_object_s *to)
293 {
294   tmpfs_unlock_reentrant(&to->to_exclsem);
295 }
296 
297 /****************************************************************************
298  * Name: tmpfs_release_lockedobject
299  ****************************************************************************/
300 
tmpfs_release_lockedobject(struct tmpfs_object_s *to)301 static void tmpfs_release_lockedobject(struct tmpfs_object_s *to)
302 {
303   DEBUGASSERT(to && to->to_refs > 0);
304 
305   /* Is this a file object? */
306 
307   if (to->to_type == TMPFS_REGULAR)
308     {
309       tmpfs_release_lockedfile((struct tmpfs_file_s *)to);
310     }
311   else
312     {
313       if(to->to_refs > 0)
314         {
315           to->to_refs--;
316         }
317       tmpfs_unlock_object(to);
318     }
319 }
320 
321 /****************************************************************************
322  * Name: tmpfs_release_lockedfile
323  ****************************************************************************/
324 
tmpfs_release_lockedfile(struct tmpfs_file_s *tfo)325 static void tmpfs_release_lockedfile(struct tmpfs_file_s *tfo)
326 {
327   DEBUGASSERT(tfo && tfo->tfo_refs > 0);
328 
329   /* If there are no longer any references to the file and the file has been
330    * unlinked from its parent directory, then free the file object now.
331    */
332 
333   if (tfo->tfo_refs == 1 && (tfo->tfo_flags & TFO_FLAG_UNLINKED) != 0)
334     {
335       sem_destroy(&tfo->tfo_exclsem.ts_sem);
336       kmm_free(tfo->tfo_data);
337       kmm_free(tfo);
338     }
339 
340   /* Otherwise, just decrement the reference count on the file object */
341 
342   else
343     {
344       if(tfo->tfo_refs > 0)
345         {
346           tfo->tfo_refs--;
347         }
348       tmpfs_unlock_file(tfo);
349     }
350 }
351 
352 /****************************************************************************
353  * Name: tmpfs_find_dirent
354  ****************************************************************************/
355 
tmpfs_find_dirent(struct tmpfs_directory_s *tdo, const char *name)356 static struct tmpfs_dirent_s *tmpfs_find_dirent(struct tmpfs_directory_s *tdo,
357                                                 const char *name)
358 {
359   LOS_DL_LIST *node;
360   struct tmpfs_dirent_s *tde;
361 
362   /* Search the list of directory entries for a match */
363 
364   for (node = tdo->tdo_entry.pstNext; node != &tdo->tdo_entry; node = node->pstNext)
365     {
366       tde = (struct tmpfs_dirent_s *)node;
367       if (tde->tde_inuse == true && strcmp(tde->tde_name, name) == 0)
368         {
369           return tde;
370         }
371     }
372 
373   /* Return NULL if not found */
374 
375   return NULL;
376 }
377 
378 /****************************************************************************
379  * Name: tmpfs_remove_dirent
380  ****************************************************************************/
381 
tmpfs_remove_dirent(struct tmpfs_directory_s *tdo, struct tmpfs_object_s *to)382 static int tmpfs_remove_dirent(struct tmpfs_directory_s *tdo,
383                                struct tmpfs_object_s *to)
384 {
385   struct tmpfs_dirent_s *tde;
386 
387   /* Search the list of directory entries for a match */
388 
389   tde = to->to_dirent;
390   if (tde == NULL)
391     {
392       return -ENONET;
393     }
394 
395   /* Free the object name */
396 
397   if (tde->tde_name != NULL)
398     {
399       kmm_free(tde->tde_name);
400       tde->tde_name = NULL;
401     }
402 
403   if (tdo->tdo_count == 0)
404     {
405       LOS_ListDelete(&tde->tde_node);
406       kmm_free(tde);
407     }
408   else
409     {
410       tde->tde_inuse = false;
411       tde->tde_object = NULL;
412     }
413   if(tdo->tdo_nentries > 0)
414     {
415       tdo->tdo_nentries--;
416     }
417   return OK;
418 }
419 
420 /****************************************************************************
421  * Name: tmpfs_add_dirent
422  ****************************************************************************/
423 
tmpfs_add_dirent(struct tmpfs_directory_s **tdo, struct tmpfs_object_s *to, const char *name)424 static int tmpfs_add_dirent(struct tmpfs_directory_s **tdo,
425                             struct tmpfs_object_s *to,
426                             const char *name)
427 {
428   struct tmpfs_directory_s *parent;
429   struct tmpfs_dirent_s *tde;
430   char *newname;
431 
432   /* Copy the name string so that it will persist as long as the
433    * directory entry.
434    */
435 
436   newname = strdup(name);
437   if (newname == NULL)
438     {
439       return -ENOSPC;
440     }
441 
442   tde = (struct tmpfs_dirent_s *)malloc(sizeof(struct tmpfs_dirent_s));
443   if (tde == NULL)
444     {
445       free(newname);
446       return -ENOSPC;
447     }
448 
449   tde->tde_object = to;
450   tde->tde_name   = newname;
451   tde->tde_inuse  = true;
452   to->to_dirent   = tde;
453 
454   /* Save the new object info in the new directory entry */
455 
456   parent = *tdo;
457   LOS_ListTailInsert(&parent->tdo_entry, &tde->tde_node);
458   parent->tdo_nentries++;
459 
460   /* Update directory times */
461 
462   parent->tdo_ctime = parent->tdo_mtime = tmpfs_timestamp();
463 
464   return OK;
465 }
466 
467 /****************************************************************************
468  * Name: tmpfs_alloc_file
469  ****************************************************************************/
470 
tmpfs_alloc_file(void)471 static struct tmpfs_file_s *tmpfs_alloc_file(void)
472 {
473   struct tmpfs_file_s *tfo;
474   size_t allocsize;
475 
476   /* Create a new zero length file object */
477 
478   allocsize = sizeof(struct tmpfs_file_s);
479   tfo = (struct tmpfs_file_s *)kmm_malloc(allocsize);
480   if (tfo == NULL)
481     {
482       return NULL;
483     }
484 
485   /* Initialize the new file object.  NOTE that the initial state is
486    * locked with one reference count.
487    */
488 
489   tfo->tfo_atime = tmpfs_timestamp();
490   tfo->tfo_mtime = tfo->tfo_atime;
491   tfo->tfo_ctime = tfo->tfo_atime;
492   tfo->tfo_type  = TMPFS_REGULAR;
493   tfo->tfo_refs  = 1;
494   tfo->tfo_flags = 0;
495   tfo->tfo_size  = 0;
496   tfo->tfo_data  = NULL;
497 
498   tfo->tfo_exclsem.ts_holder = getpid();
499   tfo->tfo_exclsem.ts_count  = 1;
500   if (sem_init(&tfo->tfo_exclsem.ts_sem, 0, 0) != 0)
501     {
502       PRINT_ERR("%s %d, sem_init failed!\n", __FUNCTION__, __LINE__);
503       kmm_free(tfo);
504       return NULL;
505     }
506 
507   return tfo;
508 }
509 
510 /****************************************************************************
511  * Name: tmpfs_create_file
512  ****************************************************************************/
513 
tmpfs_create_file(struct tmpfs_s *fs, const char *relpath, struct tmpfs_directory_s *parent_input, struct tmpfs_file_s **tfo)514 static int tmpfs_create_file(struct tmpfs_s *fs,
515                              const char *relpath,
516                              struct tmpfs_directory_s *parent_input,
517                              struct tmpfs_file_s **tfo)
518 {
519   struct tmpfs_directory_s *parent;
520   struct tmpfs_file_s *newtfo;
521   struct tmpfs_dirent_s *tde;
522   char *copy;
523   int ret;
524 
525   /* Duplicate the path variable so that we can modify it */
526 
527   copy = strdup(relpath);
528   if (copy == NULL)
529     {
530       return -ENOSPC;
531     }
532 
533   /* Separate the path into the file name and the path to the parent
534    * directory.
535    */
536   if (parent_input == NULL)
537     {
538       /* No subdirectories... use the root directory */
539       parent = (struct tmpfs_directory_s *)fs->tfs_root.tde_object;
540     }
541   else
542     {
543       parent = parent_input;
544     }
545   if (parent == NULL)
546     {
547       ret = -EEXIST;
548       goto errout_with_copy;
549     }
550   tmpfs_lock_directory(parent);
551   parent->tdo_refs++;
552 
553   /* Verify that no object of this name already exists in the directory */
554   tde = tmpfs_find_dirent(parent, copy);
555   if (tde != NULL)
556     {
557       /* Something with this name already exists in the directory.
558        * OR perhaps some fatal error occurred.
559        */
560 
561       ret = -EEXIST;
562       goto errout_with_parent;
563     }
564 
565   /* Allocate an empty file.  The initial state of the file is locked with one
566    * reference count.
567    */
568 
569   newtfo = tmpfs_alloc_file();
570   if (newtfo == NULL)
571     {
572       ret = -ENOSPC;
573       goto errout_with_parent;
574     }
575 
576   /* Then add the new, empty file to the directory */
577 
578   ret = tmpfs_add_dirent(&parent, (struct tmpfs_object_s *)newtfo, copy);
579   if (ret < 0)
580     {
581       goto errout_with_file;
582     }
583 
584   /* Release the reference and lock on the parent directory */
585   if (parent->tdo_refs > 0)
586     {
587       parent->tdo_refs--;
588     }
589   tmpfs_unlock_directory(parent);
590 
591   /* Free the copy of the relpath and return success */
592 
593   kmm_free(copy);
594   *tfo = newtfo;
595   return OK;
596 
597   /* Error exits */
598 
599 errout_with_file:
600   sem_destroy(&newtfo->tfo_exclsem.ts_sem);
601   kmm_free(newtfo);
602 
603 errout_with_parent:
604   if (parent->tdo_refs > 0)
605   {
606     parent->tdo_refs--;
607   }
608   tmpfs_unlock_directory(parent);
609 
610 errout_with_copy:
611   kmm_free(copy);
612   return ret;
613 }
614 
615 /****************************************************************************
616  * Name: tmpfs_alloc_directory
617  ****************************************************************************/
618 
tmpfs_alloc_directory(void)619 static struct tmpfs_directory_s *tmpfs_alloc_directory(void)
620 {
621   struct tmpfs_directory_s *tdo;
622   size_t allocsize;
623 
624   allocsize = sizeof(struct tmpfs_directory_s);
625   tdo = (struct tmpfs_directory_s *)kmm_malloc(allocsize);
626   if (tdo == NULL)
627     {
628       return NULL;
629     }
630 
631   /* Initialize the new directory object */
632 
633   tdo->tdo_atime    = tmpfs_timestamp();
634   tdo->tdo_mtime    = tdo->tdo_mtime;
635   tdo->tdo_ctime    =  tdo->tdo_mtime;
636   tdo->tdo_type     = TMPFS_DIRECTORY;
637   tdo->tdo_refs     = 0;
638   tdo->tdo_nentries = 0;
639   tdo->tdo_count    = 0;
640   LOS_ListInit(&tdo->tdo_entry);
641 
642   tdo->tdo_exclsem.ts_holder = TMPFS_NO_HOLDER;
643   tdo->tdo_exclsem.ts_count  = 0;
644   if (sem_init(&tdo->tdo_exclsem.ts_sem, 0, 1) != 0)
645     {
646       PRINT_ERR("%s %d, sem_init failed!\n", __FUNCTION__, __LINE__);
647       kmm_free(tdo);
648       return NULL;
649     }
650   return tdo;
651 }
652 
653 /****************************************************************************
654  * Name: tmpfs_create_directory
655  ****************************************************************************/
656 
tmpfs_create_directory(struct tmpfs_s *fs, const char *relpath, struct tmpfs_directory_s *parent_input, struct tmpfs_directory_s **tdo)657 static int tmpfs_create_directory(struct tmpfs_s *fs,
658                                   const char *relpath,
659                                   struct tmpfs_directory_s *parent_input,
660                                   struct tmpfs_directory_s **tdo)
661 {
662   struct tmpfs_directory_s *parent;
663   struct tmpfs_directory_s *newtdo;
664   struct tmpfs_dirent_s *tde;
665   char *copy;
666   int ret;
667 
668   /* Duplicate the path variable so that we can modify it */
669 
670   copy = strdup(relpath);
671   if (copy == NULL)
672     {
673       return -ENOSPC;
674     }
675 
676   /* Separate the path into the file name and the path to the parent
677    * directory.
678    */
679   if (parent_input == NULL)
680     {
681       /* No subdirectories... use the root directory */
682 
683       parent = (struct tmpfs_directory_s *)fs->tfs_root.tde_object;
684     }
685   else
686     {
687       parent = parent_input;
688     }
689 
690   /* Verify that no object of this name already exists in the directory */
691   if (parent == NULL)
692     {
693       ret = -EEXIST;
694       goto errout_with_copy;
695     }
696   tmpfs_lock_directory(parent);
697   parent->tdo_refs++;
698   tde = tmpfs_find_dirent(parent, copy);
699   if (tde != NULL)
700     {
701       /* Something with this name already exists in the directory.
702        * OR perhaps some fatal error occurred.
703        */
704 
705       ret = -EEXIST;
706 
707       goto errout_with_parent;
708     }
709 
710   /* Allocate an empty directory object.  NOTE that there is no reference on
711    * the new directory and the object is not locked.
712    */
713 
714   newtdo = tmpfs_alloc_directory();
715   if (newtdo == NULL)
716     {
717       ret = -ENOSPC;
718       goto errout_with_parent;
719     }
720 
721   /* Then add the new, empty file to the directory */
722 
723   ret = tmpfs_add_dirent(&parent, (struct tmpfs_object_s *)newtdo, copy);
724   if (ret < 0)
725     {
726       goto errout_with_directory;
727     }
728 
729   /* Free the copy of the relpath, release our reference to the parent directory,
730    * and return success
731    */
732   if (parent->tdo_refs > 0)
733     {
734       parent->tdo_refs--;
735     }
736   tmpfs_unlock_directory(parent);
737   kmm_free(copy);
738 
739   /* Return the (unlocked, unreferenced) directory object to the caller */
740 
741   if (tdo != NULL)
742     {
743       *tdo = newtdo;
744     }
745 
746   return OK;
747 
748   /* Error exits */
749 
750 errout_with_directory:
751   sem_destroy(&newtdo->tdo_exclsem.ts_sem);
752   kmm_free(newtdo);
753 
754 errout_with_parent:
755   if (parent->tdo_refs > 0)
756     {
757       parent->tdo_refs--;
758     }
759   tmpfs_unlock_directory(parent);
760 
761 errout_with_copy:
762   kmm_free(copy);
763   return ret;
764 }
765 
766 /****************************************************************************
767  * Name: tmpfs_find_object
768  ****************************************************************************/
769 
tmpfs_find_object(struct tmpfs_s *fs, const char *relpath, struct tmpfs_object_s **object, struct tmpfs_directory_s **parent)770 static int tmpfs_find_object(struct tmpfs_s *fs,
771                              const char *relpath,
772                              struct tmpfs_object_s **object,
773                              struct tmpfs_directory_s **parent)
774 {
775   struct tmpfs_object_s *to = NULL;
776   struct tmpfs_dirent_s *tde;
777   struct tmpfs_directory_s *tdo = NULL;
778   struct tmpfs_directory_s *next_tdo;
779   char *segment;
780   char *next_segment;
781   char *tkptr;
782   char *copy;
783 
784   /* Make a copy of the path (so that we can modify it via strtok) */
785 
786   copy = strdup(relpath);
787   if (copy == NULL)
788     {
789       return -ENOSPC;
790     }
791 
792   /* Traverse the file system for any object with the matching name */
793 
794   to       = fs->tfs_root.tde_object;
795   next_tdo = (struct tmpfs_directory_s *)fs->tfs_root.tde_object;
796   tdo      = next_tdo;
797   for (segment =  strtok_r(copy, "/", &tkptr);
798        segment != NULL;
799        segment = next_segment)
800     {
801       /* Get the next segment after the one we are currently working on.
802        * This will be NULL is we are working on the final segment of the
803        * relpath.
804        */
805 
806       next_segment = strtok_r(NULL, "/", &tkptr);
807 
808       /* Search the next directory. */
809 
810       tdo = next_tdo;
811 
812       /* Find the TMPFS object with the next segment name in the current
813        * directory.
814        */
815 
816       tde = tmpfs_find_dirent(tdo, segment);
817       if (tde == NULL)
818         {
819           /* No object with this name exists in the directory. */
820 
821           kmm_free(copy);
822           return -ENOENT;
823         }
824 
825       to = tde->tde_object;
826 
827       /* Is this object another directory? */
828 
829       if (to->to_type != TMPFS_DIRECTORY)
830         {
831           /* No.  Was this the final segment in the path? */
832 
833           if (next_segment == NULL)
834             {
835               /* Then we can break out of the loop now */
836 
837                break;
838             }
839 
840           /* No, this was not the final segement of the relpath.
841            * We cannot continue the search if any of the intermediate
842            * segments do no correspond to directories.
843            */
844 
845           kmm_free(copy);
846           return -ENOTDIR;
847         }
848 
849       /* Search this directory for the next segement.  If we
850        * exit the loop, tdo will still refer to the parent
851        * directory of to.
852        */
853 
854       next_tdo = (struct tmpfs_directory_s *)to;
855     }
856 
857   /* When we exit this loop (successfully), to will point to the TMPFS
858    * object associated with the terminal segment of the relpath.
859    * Increment the reference count on the located object.
860    */
861 
862   /* Free the dup'ed string */
863 
864   kmm_free(copy);
865 
866   /* Return what we found */
867 
868   if (parent)
869     {
870       if (tdo != NULL)
871         {
872           /* Get exclusive access to the parent and increment the reference
873            * count on the object.
874            */
875 
876           tmpfs_lock_directory(tdo);
877           tdo->tdo_refs++;
878         }
879 
880       *parent = tdo;
881     }
882 
883   if (object)
884     {
885       if (to != NULL)
886         {
887           /* Get exclusive access to the object and increment the reference
888            * count on the object.
889            */
890 
891           tmpfs_lock_object(to);
892           to->to_refs++;
893         }
894 
895       *object = to;
896     }
897 
898   return OK;
899 }
900 
901 /****************************************************************************
902  * Name: tmpfs_find_file
903  ****************************************************************************/
904 
tmpfs_find_file(struct tmpfs_s *fs, const char *relpath, struct tmpfs_file_s **tfo, struct tmpfs_directory_s **parent)905 static int tmpfs_find_file(struct tmpfs_s *fs,
906                            const char *relpath,
907                            struct tmpfs_file_s **tfo,
908                            struct tmpfs_directory_s **parent)
909 {
910   struct tmpfs_object_s *to;
911   int ret;
912 
913   /* Find the object at this path.  If successful, tmpfs_find_object() will
914    * lock both the object and the parent directory and will increment the
915    * reference count on both.
916    */
917 
918   ret = tmpfs_find_object(fs, relpath, &to, parent);
919   if (ret >= 0)
920     {
921       /* We found it... but is it a regular file? */
922 
923       if (to->to_type != TMPFS_REGULAR)
924         {
925           /* No... unlock the object and its parent and return an error */
926 
927           tmpfs_release_lockedobject(to);
928 
929           if (parent)
930             {
931               struct tmpfs_directory_s *tdo = *parent;
932 
933               tdo->tdo_refs--;
934               tmpfs_unlock_directory(tdo);
935             }
936 
937           ret = -EISDIR;
938         }
939 
940       /* Return the verified file object */
941 
942       *tfo = (struct tmpfs_file_s *)to;
943     }
944 
945   return ret;
946 }
947 
948 /****************************************************************************
949  * Name: tmpfs_find_directory
950  ****************************************************************************/
951 
tmpfs_find_directory(struct tmpfs_s *fs, const char *relpath, struct tmpfs_directory_s **tdo, struct tmpfs_directory_s **parent)952 static int tmpfs_find_directory(struct tmpfs_s *fs,
953                            const char *relpath,
954                            struct tmpfs_directory_s **tdo,
955                            struct tmpfs_directory_s **parent)
956 {
957   struct tmpfs_object_s *to;
958   int ret;
959 
960   /* Find the object at this path */
961 
962   ret = tmpfs_find_object(fs, relpath, &to, parent);
963   if (ret >= 0)
964     {
965       /* We found it... but is it a regular file? */
966 
967       if (to->to_type != TMPFS_DIRECTORY)
968         {
969           /* No... unlock the object and its parent and return an error */
970 
971           tmpfs_release_lockedobject(to);
972 
973           if (parent)
974             {
975               struct tmpfs_directory_s *tmptdo = *parent;
976 
977               tmptdo->tdo_refs--;
978               tmpfs_unlock_directory(tmptdo);
979             }
980 
981           ret = -ENOTDIR;
982         }
983 
984       /* Return the verified file object */
985 
986       *tdo = (struct tmpfs_directory_s *)to;
987     }
988 
989   return ret;
990 }
991 
992 /****************************************************************************
993  * Name: tmpfs_close
994  ****************************************************************************/
995 
tmpfs_close(struct file *filep)996 int tmpfs_close(struct file *filep)
997 {
998   struct tmpfs_file_s *tfo;
999 
1000   DEBUGASSERT(filep != NULL);
1001 
1002   /* Recover our private data from the struct file instance */
1003   tfo = (struct tmpfs_file_s *)(filep->f_vnode->data);
1004   if (tfo == NULL)
1005     {
1006       return -EINVAL;
1007     }
1008   /* Get exclusive access to the file */
1009 
1010   tmpfs_lock_file(tfo);
1011 
1012   /* Decrement the reference count on the file */
1013 
1014   if (tfo->tfo_refs > 0)
1015     {
1016       tfo->tfo_refs--;
1017     }
1018 
1019   /* If the reference count decremented to zero and the file has been
1020    * unlinked, then free the file allocation now.
1021    */
1022 
1023   if (tfo->tfo_refs == 0 && (tfo->tfo_flags & TFO_FLAG_UNLINKED) != 0)
1024     {
1025       /* Free the file object while we hold the lock?  Weird but this
1026        * should be safe because the object is unlinked and could not
1027        * have any other references.
1028        */
1029 
1030       (void)sem_destroy(&tfo->tfo_exclsem.ts_sem);
1031       kmm_free(tfo->tfo_data);
1032       kmm_free(tfo);
1033       return OK;
1034     }
1035 
1036   /* Release the lock on the file */
1037 
1038   tmpfs_unlock_file(tfo);
1039   return OK;
1040 }
1041 
1042 /****************************************************************************
1043  * Name: tmpfs_read
1044  ****************************************************************************/
1045 
tmpfs_read(struct file *filep, char *buffer, size_t buflen)1046 ssize_t tmpfs_read(struct file *filep, char *buffer, size_t buflen)
1047 {
1048   struct tmpfs_file_s *tfo;
1049   ssize_t nread;
1050   loff_t startpos;
1051   loff_t endpos;
1052 
1053   DEBUGASSERT(filep->f_vnode != NULL);
1054 
1055   /* Recover our private data from the struct file instance */
1056 
1057   tfo = (struct tmpfs_file_s *)(filep->f_vnode->data);
1058   if (tfo == NULL)
1059     {
1060       return -EINVAL;
1061     }
1062   if (filep->f_pos >= tfo->tfo_size || buflen == 0)
1063     {
1064       return 0;
1065     }
1066 
1067   /* Get exclusive access to the file */
1068 
1069   tmpfs_lock_file(tfo);
1070 
1071   /* Handle attempts to read beyond the end of the file. */
1072 
1073   startpos = filep->f_pos;
1074   nread    = buflen;
1075   endpos   = startpos + buflen;
1076 
1077   if (endpos > tfo->tfo_size)
1078     {
1079       endpos = tfo->tfo_size;
1080       nread  = endpos - startpos;
1081     }
1082 
1083   /* Copy data from the memory object to the user buffer */
1084 
1085   if (LOS_CopyFromKernel(buffer, buflen, &tfo->tfo_data[startpos], nread) != 0)
1086     {
1087       tmpfs_unlock_file(tfo);
1088       return -EINVAL;
1089     }
1090   filep->f_pos += nread;
1091 
1092   /* Update the node's access time */
1093 
1094   tfo->tfo_atime = tmpfs_timestamp();
1095 
1096   /* Release the lock on the file */
1097 
1098   tmpfs_unlock_file(tfo);
1099   return nread;
1100 }
1101 
1102 /****************************************************************************
1103  * Name: tmpfs_readpage
1104  ****************************************************************************/
1105 
tmpfs_readpage(struct Vnode *vnode, char *buffer, off_t off)1106 ssize_t tmpfs_readpage(struct Vnode *vnode, char *buffer, off_t off)
1107 {
1108   struct tmpfs_file_s *tfo;
1109   ssize_t nread;
1110   loff_t startpos;
1111   loff_t endpos;
1112 
1113   DEBUGASSERT(vnode->data != NULL);
1114 
1115   /* Recover our private data from the vnode */
1116 
1117   tfo = (struct tmpfs_file_s *)(vnode->data);
1118   if (tfo == NULL)
1119     {
1120       return -EINVAL;
1121     }
1122   if (off >= tfo->tfo_size)
1123     {
1124       return 0;
1125     }
1126 
1127   /* Get exclusive access to the file */
1128 
1129   tmpfs_lock_file(tfo);
1130 
1131   /* Handle attempts to read beyond the end of the file. */
1132 
1133   startpos = off;
1134   nread    = PAGE_SIZE;
1135   endpos   = startpos + PAGE_SIZE;
1136 
1137   if (endpos > tfo->tfo_size)
1138     {
1139       endpos = tfo->tfo_size;
1140       nread  = endpos - startpos;
1141     }
1142 
1143   /* Copy data from the memory object to the user buffer */
1144 
1145   if (LOS_CopyFromKernel(buffer, PAGE_SIZE, &tfo->tfo_data[startpos], nread) != 0)
1146     {
1147       tmpfs_unlock_file(tfo);
1148       return -EINVAL;
1149     }
1150 
1151   /* Update the node's access time */
1152 
1153   tfo->tfo_atime = tmpfs_timestamp();
1154 
1155   /* Release the lock on the file */
1156 
1157   tmpfs_unlock_file(tfo);
1158   return nread;
1159 }
1160 
1161 
1162 /****************************************************************************
1163  * Name: tmpfs_create
1164  ****************************************************************************/
1165 
tmpfs_create(struct Vnode *dvp, const char *path, int mode, struct Vnode **vpp)1166 int tmpfs_create(struct Vnode *dvp, const char *path, int mode, struct Vnode **vpp)
1167 {
1168     struct Vnode *vp = NULL;
1169     struct tmpfs_file_s *tfo;
1170     struct tmpfs_s *fs;
1171     int ret = 0;
1172     struct tmpfs_directory_s *parent_tdo = NULL;
1173 
1174     if (dvp == NULL)
1175       {
1176         return -ENOENT;
1177       }
1178 
1179     fs = dvp->originMount->data;
1180     if (fs == NULL)
1181       {
1182         return -ENOENT;
1183       }
1184 
1185     tmpfs_lock(fs);
1186 
1187     if (dvp->data != NULL)
1188       {
1189         parent_tdo = (struct tmpfs_directory_s *)(dvp->data);
1190       }
1191 
1192     ret = tmpfs_create_file(fs, path, parent_tdo, &tfo);
1193     if (ret < 0)
1194       {
1195         goto errout_with_fslock;
1196       }
1197 
1198     ret = VnodeAlloc(&tmpfs_vops, &vp);
1199     if (ret != 0)
1200       {
1201         tmpfs_unlock_file(tfo);
1202         goto errout_with_fslock;
1203       }
1204     vp->parent = dvp;
1205     vp->vop = dvp->vop;
1206     vp->fop = dvp->fop;
1207     vp->data = tfo;
1208     vp->originMount = dvp->originMount;
1209     vp->type = VNODE_TYPE_REG;
1210     tfo->mode = mode;
1211     vp->mode = tfo->mode;
1212     vp->gid = tfo->gid;
1213     vp->uid = tfo->uid;
1214 
1215     ret = VfsHashInsert(vp, (uint32_t)tfo);
1216 
1217     *vpp = vp;
1218     tmpfs_unlock_file(tfo);
1219 errout_with_fslock:
1220     tmpfs_unlock(fs);
1221     return 0;
1222 }
1223 
1224 
1225 /****************************************************************************
1226  * Name: los_set_ramfs_unit
1227  ****************************************************************************/
1228 
1229 static spinlock_t tmpfs_alloc_unit_lock;
1230 bool is_tmpfs_lock_init = false;
1231 unsigned int g_tmpfs_alloc_unit = 0;
1232 
los_set_ramfs_unit(off_t size)1233 void los_set_ramfs_unit(off_t size)
1234 {
1235   if (is_tmpfs_lock_init && size >= 0)
1236     {
1237       spin_lock(&tmpfs_alloc_unit_lock);
1238       g_tmpfs_alloc_unit = size;
1239       spin_unlock(&tmpfs_alloc_unit_lock);
1240     }
1241 }
1242 
1243 /****************************************************************************
1244  * Name: tmpfs_write
1245  ****************************************************************************/
1246 
tmpfs_write(struct file *filep, const char *buffer, size_t buflen)1247 ssize_t tmpfs_write(struct file *filep, const char *buffer, size_t buflen)
1248 {
1249   struct tmpfs_file_s *tfo;
1250   ssize_t nwritten;
1251   loff_t startpos;
1252   loff_t endpos;
1253   int ret;
1254   int alloc;
1255   char *data;
1256 
1257   DEBUGASSERT(filep->f_vnode != NULL);
1258 
1259   if (buflen == 0)
1260     {
1261       return 0;
1262     }
1263 
1264   /* Recover our private data from the struct file instance */
1265   tfo = (struct tmpfs_file_s *)(filep->f_vnode->data);
1266   if (tfo == NULL)
1267     {
1268       return -EINVAL;
1269     }
1270   /* Get exclusive access to the file */
1271 
1272   tmpfs_lock_file(tfo);
1273 
1274   /* Handle attempts to write beyond the end of the file */
1275 
1276   startpos = filep->f_pos;
1277   nwritten = buflen;
1278   endpos   = startpos + buflen;
1279 
1280   if (startpos < 0)
1281     {
1282       ret = -EPERM;
1283       goto errout_with_lock;
1284     }
1285 
1286   if (endpos > tfo->tfo_size)
1287     {
1288       spin_lock(&tmpfs_alloc_unit_lock);
1289       alloc = (g_tmpfs_alloc_unit > buflen) ? g_tmpfs_alloc_unit : buflen;
1290       spin_unlock(&tmpfs_alloc_unit_lock);
1291       data  = (char *)malloc(startpos + alloc);
1292       if (!data)
1293         {
1294           ret = -ENOSPC;
1295           goto errout_with_lock;
1296         }
1297       if (tfo->tfo_size)
1298         {
1299           ret = memcpy_s(data, startpos + alloc, tfo->tfo_data, tfo->tfo_size);
1300           if (ret != EOK)
1301             {
1302               ret = -1;
1303               free(data);
1304               goto errout_with_lock;
1305             }
1306           free(tfo->tfo_data);
1307         }
1308       if (startpos > tfo->tfo_size)
1309         {
1310           (void)memset_s(data + tfo->tfo_size, startpos + alloc - tfo->tfo_size, 0, startpos - tfo->tfo_size);
1311         }
1312 
1313       tfo->tfo_data = data;
1314       tfo->tfo_size = startpos + alloc;
1315     }
1316 
1317   /* Copy data from the memory object to the user buffer */
1318   if (LOS_CopyToKernel(&tfo->tfo_data[startpos], nwritten, buffer, nwritten) != 0)
1319     {
1320       ret = -EINVAL;
1321       goto errout_with_lock;
1322     }
1323   filep->f_pos += nwritten;
1324 
1325   /* Update the modified and access times of the node */
1326 
1327   tfo->tfo_ctime = tfo->tfo_mtime = tmpfs_timestamp();
1328 
1329   /* Release the lock on the file */
1330 
1331   tmpfs_unlock_file(tfo);
1332   return nwritten;
1333 
1334 errout_with_lock:
1335   tmpfs_unlock_file(tfo);
1336   return (ssize_t)ret;
1337 }
1338 
1339 /****************************************************************************
1340  * Name: tmpfs_seek
1341  ****************************************************************************/
1342 
tmpfs_seek(struct file *filep, off_t offset, int whence)1343 off_t tmpfs_seek(struct file *filep, off_t offset, int whence)
1344 {
1345   struct tmpfs_file_s *tfo;
1346   off_t position;
1347 
1348   DEBUGASSERT(filep->f_vnode != NULL);
1349 
1350   /* Recover our private data from the struct file instance */
1351 
1352   tfo = (struct tmpfs_file_s *)(filep->f_vnode->data);
1353   if (tfo == NULL)
1354     {
1355       return -EINVAL;
1356     }
1357   /* Map the offset according to the whence option */
1358 
1359   switch (whence)
1360     {
1361       case SEEK_SET: /* The offset is set to offset bytes. */
1362           position = offset;
1363           break;
1364 
1365       case SEEK_CUR: /* The offset is set to its current location plus
1366                       * offset bytes. */
1367           position = offset + filep->f_pos;
1368           break;
1369 
1370       case SEEK_END: /* The offset is set to the size of the file plus
1371                       * offset bytes. */
1372           position = offset + tfo->tfo_size;
1373           break;
1374 
1375       default:
1376           return -EINVAL;
1377     }
1378 
1379   /* Attempts to set the position beyond the end of file will
1380    * work if the file is open for write access.
1381    *
1382    * REVISIT: This simple implementation has no per-open storage that
1383    * would be needed to retain the open flags.
1384    */
1385     if (position < 0)
1386     {
1387       return -EINVAL;
1388     }
1389 
1390   /* Save the new file position */
1391 
1392   filep->f_pos = position;
1393   return position;
1394 }
1395 
1396 /****************************************************************************
1397  * Name: tmpfs_ioctl
1398  ****************************************************************************/
1399 
tmpfs_ioctl(struct file *filep, int cmd, unsigned long arg)1400 int tmpfs_ioctl(struct file *filep, int cmd, unsigned long arg)
1401 {
1402   return -EINVAL;
1403 }
1404 
1405 /****************************************************************************
1406  * Name: tmpfs_sync
1407  ****************************************************************************/
1408 
tmpfs_sync(struct file *filep)1409 int tmpfs_sync(struct file *filep)
1410 {
1411   return 0;
1412 }
1413 
1414 /****************************************************************************
1415  * Name: tmpfs_opendir
1416  ****************************************************************************/
1417 
tmpfs_opendir(struct Vnode *vp, struct fs_dirent_s *dir)1418 int tmpfs_opendir(struct Vnode *vp, struct fs_dirent_s *dir)
1419 {
1420   struct tmpfs_s *fs;
1421   struct tmpfs_directory_s *tdo;
1422   int ret = 0;
1423   struct fs_tmpfsdir_s *tmp;
1424 
1425   DEBUGASSERT(vp != NULL && dir != NULL);
1426 
1427   /* Get the mountpoint private data from the inode structure */
1428 
1429   fs = vp->originMount->data;
1430   DEBUGASSERT(fs != NULL);
1431 
1432   tmp = (struct fs_tmpfsdir_s *)malloc(sizeof(struct fs_tmpfsdir_s));
1433   if (!tmp)
1434     {
1435       return -ENOSPC;
1436     }
1437 
1438   /* Get exclusive access to the file system */
1439 
1440   tmpfs_lock(fs);
1441 
1442   /* Find the directory object associated with this relative path.
1443    * If successful, this action will lock both the parent directory and
1444    * the file object, adding one to the reference count of both.
1445    * In the event that -ENOENT, there will still be a reference and
1446    * lock on the returned directory.
1447    */
1448 
1449   if (vp->data != NULL)
1450     {
1451       tdo = (struct tmpfs_directory_s *)vp->data;
1452     }
1453   else
1454     {
1455       tdo = (struct tmpfs_directory_s *)fs->tfs_root.tde_object;
1456     }
1457 
1458   if (tdo == NULL)
1459     {
1460       free(tmp);
1461       tmpfs_unlock(fs);
1462       return -EINTR;
1463     }
1464   tmpfs_lock_directory(tdo);
1465   tmp->tf_tdo   = tdo;
1466   tmp->tf_index = 0;
1467   dir->u.fs_dir = (fs_dir_s)tmp;
1468   tdo->tdo_count++;
1469   tdo->tdo_refs++;
1470   tmpfs_unlock_directory(tdo);
1471 
1472   /* Release the lock on the file system and return the result */
1473 
1474   tmpfs_unlock(fs);
1475   return ret;
1476 }
1477 
1478 /****************************************************************************
1479  * Name: tmpfs_closedir
1480  ****************************************************************************/
1481 
tmpfs_closedir(struct Vnode *vp, struct fs_dirent_s *dir)1482 int tmpfs_closedir(struct Vnode *vp, struct fs_dirent_s *dir)
1483 {
1484   struct tmpfs_directory_s *tdo;
1485   struct fs_tmpfsdir_s *tmp;
1486 
1487   DEBUGASSERT(vp != NULL && dir != NULL);
1488 
1489   /* Get the directory structure from the dir argument */
1490 
1491   tmp = (struct fs_tmpfsdir_s *)dir->u.fs_dir;
1492   if (tmp == NULL)
1493     {
1494       return -ENOENT;
1495     }
1496   tdo = tmp->tf_tdo;
1497   DEBUGASSERT(tdo != NULL);
1498 
1499   /* Decrement the reference count on the directory object */
1500 
1501   tmpfs_lock_directory(tdo);
1502   if (tdo->tdo_count == 1)
1503     {
1504       LOS_DL_LIST *node = tdo->tdo_entry.pstNext;
1505       struct tmpfs_dirent_s *tde;
1506       while (node != &tdo->tdo_entry)
1507         {
1508           tde = (struct tmpfs_dirent_s *)node;
1509           node = node->pstNext;
1510           if (tde->tde_inuse == false)
1511             {
1512               LOS_ListDelete(&tde->tde_node);
1513               kmm_free(tde);
1514             }
1515         }
1516     }
1517   if (tdo->tdo_refs > 0)
1518     {
1519       tdo->tdo_refs--;
1520     }
1521   if (tdo->tdo_count > 0)
1522     {
1523       tdo->tdo_count--;
1524     }
1525   tmpfs_unlock_directory(tdo);
1526 
1527   free(tmp);
1528 
1529   return OK;
1530 }
1531 
1532 /****************************************************************************
1533  * Name: tmpfs_readdir
1534  ****************************************************************************/
1535 
tmpfs_readdir(struct Vnode *vp, struct fs_dirent_s *dir)1536 int tmpfs_readdir(struct Vnode *vp, struct fs_dirent_s *dir)
1537 {
1538   struct tmpfs_directory_s *tdo;
1539   unsigned int index;
1540   int ret;
1541   struct fs_tmpfsdir_s *tmp;
1542   LOS_DL_LIST *node;
1543   struct tmpfs_dirent_s *tde;
1544 
1545   DEBUGASSERT(vp != NULL && dir != NULL);
1546 
1547   /* Get the directory structure from the dir argument and lock it */
1548 
1549   tmp = (struct fs_tmpfsdir_s *)dir->u.fs_dir;
1550   if (tmp == NULL)
1551     {
1552       return -ENOENT;
1553     }
1554 
1555   tdo = tmp->tf_tdo;
1556   if (tdo == NULL)
1557     {
1558       return -ENOENT;
1559     }
1560 
1561   tmpfs_lock_directory(tdo);
1562 
1563   /* Have we reached the end of the directory? */
1564 
1565   index = tmp->tf_index;
1566   node = tdo->tdo_entry.pstNext;
1567   while (node != &tdo->tdo_entry && index != 0)
1568     {
1569        node = node->pstNext;
1570        index--;
1571     }
1572 
1573   while (node != &tdo->tdo_entry)
1574     {
1575       tde = (struct tmpfs_dirent_s *)node;
1576       tmp->tf_index++;
1577       if (tde->tde_inuse == true)
1578        {
1579          break;
1580        }
1581        node = node->pstNext;
1582     }
1583 
1584   if (node == &tdo->tdo_entry)
1585     {
1586       /* We signal the end of the directory by returning the special error:
1587        * -ENOENT
1588        */
1589 
1590       PRINT_INFO("End of directory\n");
1591       ret = -ENOENT;
1592     }
1593   else
1594     {
1595       struct tmpfs_object_s *to;
1596 
1597       /* Does this entry refer to a file or a directory object? */
1598 
1599       to  = tde->tde_object;
1600       DEBUGASSERT(to != NULL);
1601 
1602       if (to->to_type == TMPFS_DIRECTORY)
1603         {
1604           /* A directory */
1605 
1606            dir->fd_dir[0].d_type = DT_DIR;
1607         }
1608       else /* to->to_type == TMPFS_REGULAR) */
1609         {
1610           /* A regular file */
1611 
1612            dir->fd_dir[0].d_type = DT_REG;
1613         }
1614 
1615       /* Copy the entry name */
1616 
1617       (void)strncpy_s(dir->fd_dir[0].d_name, NAME_MAX + 1, tde->tde_name, NAME_MAX);
1618 
1619       dir->fd_position++;
1620       dir->fd_dir[0].d_off = dir->fd_position;
1621       dir->fd_dir[0].d_reclen = (uint16_t)sizeof(struct dirent);
1622 
1623       ret = 1; // 1 means current file num is 1
1624     }
1625 
1626   tmpfs_unlock_directory(tdo);
1627   return ret;
1628 }
1629 
1630 /****************************************************************************
1631  * Name: tmpfs_rewinddir
1632  ****************************************************************************/
1633 
tmpfs_rewinddir(struct Vnode *vp, struct fs_dirent_s *dir)1634 int tmpfs_rewinddir(struct Vnode *vp, struct fs_dirent_s *dir)
1635 {
1636   struct fs_tmpfsdir_s *tmp;
1637   PRINT_DEBUG("vp: %p dir: %p\n",  vp, dir);
1638   DEBUGASSERT(vp != NULL && dir != NULL);
1639   tmp = (struct fs_tmpfsdir_s *)dir->u.fs_dir;
1640 
1641   /* Set the readdir index to zero */
1642 
1643   tmp->tf_index = 0;
1644   return OK;
1645 }
1646 
tmpfs_truncate(struct Vnode *vp, off_t len)1647 int tmpfs_truncate(struct Vnode *vp, off_t len)
1648 {
1649   struct tmpfs_file_s *tfo = NULL;
1650 
1651   tfo = vp->data;
1652   tfo->tfo_size = 0;
1653 
1654   if (tfo->tfo_data)
1655     {
1656       free(tfo->tfo_data);
1657     }
1658 
1659   return OK;
1660 }
1661 
1662 
1663 /****************************************************************************
1664  * Name: tmpfs_mount
1665  ****************************************************************************/
1666 
tmpfs_mount(struct Mount *mnt, struct Vnode *device, const void *data)1667 int tmpfs_mount(struct Mount *mnt, struct Vnode *device, const void *data)
1668 {
1669   struct tmpfs_directory_s *tdo;
1670   struct tmpfs_s *fs = &tmpfs_superblock;
1671   struct Vnode *vp = NULL;
1672   int ret;
1673 
1674   DEBUGASSERT(device == NULL);
1675 
1676   if (fs->tfs_root.tde_object != NULL)
1677     {
1678       return -EPERM;
1679     }
1680 
1681   /* Create a root file system.  This is like a single directory entry in
1682    * the file system structure.
1683    */
1684 
1685   tdo = tmpfs_alloc_directory();
1686   if (tdo == NULL)
1687     {
1688       return -ENOSPC;
1689     }
1690 
1691   LOS_ListInit(&fs->tfs_root.tde_node);
1692   fs->tfs_root.tde_object = (struct tmpfs_object_s *)tdo;
1693   fs->tfs_root.tde_name   = NULL;
1694 
1695   /* Set up the backward link (to support reallocation) */
1696 
1697   tdo->tdo_dirent         = &fs->tfs_root;
1698 
1699   /* Initialize the file system state */
1700 
1701   fs->tfs_exclsem.ts_holder = TMPFS_NO_HOLDER;
1702   fs->tfs_exclsem.ts_count  = 0;
1703   sem_init(&fs->tfs_exclsem.ts_sem, 0, 1);
1704 
1705   /* Return the new file system handle */
1706 
1707   spin_lock_init(&tmpfs_alloc_unit_lock);
1708   is_tmpfs_lock_init = true;
1709 
1710   ret = VnodeAlloc(&tmpfs_vops, &vp);
1711   if (ret != 0)
1712     {
1713       ret = ENOMEM;
1714       goto ERROR_WITH_FSWIN;
1715     }
1716 
1717   tdo->mode = mnt->vnodeBeCovered->mode;
1718   tdo->gid = mnt->vnodeBeCovered->gid;
1719   tdo->uid = mnt->vnodeBeCovered->uid;
1720   vp->originMount = mnt;
1721   vp->fop = &tmpfs_fops;
1722   vp->type = VNODE_TYPE_DIR;
1723   vp->data = NULL;
1724   vp->mode = tdo->mode;
1725   vp->gid = tdo->gid;
1726   vp->uid = tdo->uid;
1727   mnt->data = fs;
1728   mnt->vnodeCovered = vp;
1729 
1730   return OK;
1731 
1732 ERROR_WITH_FSWIN:
1733   return ret;
1734 }
1735 
1736 /****************************************************************************
1737  * Name: tmpfs_unmount
1738  ****************************************************************************/
1739 
tmpfs_unmount(struct Mount *mnt, struct Vnode **blkdriver)1740 int tmpfs_unmount(struct Mount *mnt, struct Vnode **blkdriver)
1741 {
1742   struct tmpfs_s *fs = (struct tmpfs_s *)mnt->data;
1743   struct tmpfs_directory_s *tdo;
1744   int ret = 0;
1745 
1746   DEBUGASSERT(fs != NULL && fs->tfs_root.tde_object != NULL);
1747 
1748   /* Lock the file system */
1749 
1750   tmpfs_lock(fs);
1751 
1752   tdo = (struct tmpfs_directory_s *)fs->tfs_root.tde_object;
1753   if (tdo == NULL)
1754     {
1755       ret = -EINVAL;
1756       goto errout_with_objects;
1757     }
1758 
1759   if (tdo->tdo_nentries > 0 || tdo->tdo_refs > 1)
1760     {
1761       ret = -EBUSY;
1762       goto errout_with_objects;
1763     }
1764 
1765   /* Now we can destroy the root file system and the file system itself. */
1766 
1767   sem_destroy(&tdo->tdo_exclsem.ts_sem);
1768   kmm_free(tdo);
1769 
1770   sem_destroy(&fs->tfs_exclsem.ts_sem);
1771   fs->tfs_root.tde_object = NULL;
1772 
1773   tmpfs_unlock(fs);
1774   is_tmpfs_lock_init = false;
1775   return ret;
1776 
1777 errout_with_objects:
1778   tmpfs_unlock(fs);
1779   return ret;
1780 }
1781 
1782 
tmpfs_lookup(struct Vnode *parent, const char *relPath, int len, struct Vnode **vpp)1783 int tmpfs_lookup(struct Vnode *parent, const char *relPath, int len, struct Vnode **vpp)
1784 {
1785   // 1. when first time create file, lookup fail, then call tmpfs_create.
1786   // 2. when  ls, lookup will success to show.
1787   // 3. when cd, lookup success.
1788   struct tmpfs_object_s *to;
1789   struct tmpfs_s *fs;
1790   struct tmpfs_dirent_s *tde = NULL;
1791   struct tmpfs_directory_s *parent_tdo;
1792 
1793   struct Vnode *vp = NULL;
1794   int ret = 0;
1795   char filename[len + 1];
1796   ret = memcpy_s(filename, (len + 1), relPath, len);
1797   if (ret != 0)
1798     {
1799         ret = -ENOMEM;
1800         goto errout;
1801     }
1802   filename[len] = '\0';
1803 
1804   fs = parent->originMount->data;
1805   DEBUGASSERT(fs != NULL && fs->tfs_root.tde_object != NULL);
1806 
1807   tmpfs_lock(fs);
1808 
1809   parent_tdo = (struct tmpfs_directory_s *)(parent->data);
1810 
1811   if (parent_tdo == NULL)
1812     {
1813       // if parent_tdo don't exist, find file in root.
1814       ret = tmpfs_find_object(fs, filename, &to, NULL);
1815       if (ret < 0)
1816         {
1817           goto errout_with_lock;
1818         }
1819     }
1820   else
1821     {
1822       if (parent_tdo->tdo_type != TMPFS_DIRECTORY)
1823         {
1824           ret = -ENOENT;
1825           goto errout_with_lock;
1826         }
1827       // if parent_tdo exist£¬search for the relationship between parent_tdo and the current dirname.
1828       tde = tmpfs_find_dirent(parent_tdo, filename);
1829       if (tde == NULL)
1830         {
1831           ret = -ENOENT;
1832           goto errout_with_lock;
1833         }
1834       to = tde->tde_object;
1835     }
1836 
1837   if (to == NULL)
1838     {
1839       ret = -ENOENT;
1840       goto errout_with_lock;
1841     }
1842   tmpfs_lock_object(to);
1843   to->to_refs++;
1844 
1845   (void)VfsHashGet(parent->originMount, (uint32_t)to, &vp, NULL, NULL);
1846   if (vp == NULL)
1847     {
1848       ret = VnodeAlloc(&tmpfs_vops, &vp);
1849       if (ret != 0)
1850         {
1851           PRINTK("%s-%d \n", __FUNCTION__, __LINE__);
1852           goto errout_with_objects;
1853         }
1854 
1855       vp->vop = parent->vop;
1856       vp->fop = parent->fop;
1857       vp->data = to;
1858       vp->originMount = parent->originMount;
1859       vp->type = to->to_type == TMPFS_REGULAR ? VNODE_TYPE_REG : VNODE_TYPE_DIR;
1860       vp->mode = to->mode;
1861       vp->gid = to->gid;
1862       vp->uid = to->uid;
1863 
1864       ret = VfsHashInsert(vp, (uint32_t)to);
1865     }
1866   vp->parent = parent;
1867 
1868   *vpp = vp;
1869 
1870   tmpfs_release_lockedobject(to);
1871   tmpfs_unlock(fs);
1872 
1873   return 0;
1874 
1875 errout_with_objects:
1876   tmpfs_release_lockedobject(to);
1877 errout_with_lock:
1878   tmpfs_unlock(fs);
1879 errout:
1880   return ret;
1881 }
1882 
tmpfs_reclaim(struct Vnode *vp)1883 int tmpfs_reclaim(struct Vnode *vp)
1884 {
1885     vp->data = NULL;
1886     return 0;
1887 }
1888 
1889 /****************************************************************************
1890  * Name: tmpfs_statfs
1891  ****************************************************************************/
1892 
tmpfs_statfs(struct Mount *mp, struct statfs *sbp)1893 int tmpfs_statfs(struct Mount *mp, struct statfs *sbp)
1894 {
1895   (void)memset_s(sbp, sizeof(struct statfs), 0, sizeof(struct statfs));
1896 
1897   sbp->f_type = TMPFS_MAGIC;
1898   sbp->f_flags = mp->mountFlags;
1899 
1900   return OK;
1901 }
1902 
1903 /****************************************************************************
1904  * Name: tmpfs_unlink
1905  ****************************************************************************/
1906 
tmpfs_unlink(struct Vnode *parent, struct Vnode *node, const char *relpath)1907 int tmpfs_unlink(struct Vnode *parent, struct Vnode *node, const char *relpath)
1908 {
1909   struct tmpfs_s *fs;
1910   struct tmpfs_directory_s *parent_dir;
1911   struct tmpfs_file_s *tfo = NULL;
1912   int ret;
1913 
1914   PRINT_INFO("mountpt: %p node: %p relpath: %s\n", parent, node, relpath);
1915   DEBUGASSERT(parent != NULL && node != NULL && relpath != NULL);
1916 
1917   if (strlen(relpath) == 0)
1918     {
1919       return -EISDIR;
1920     }
1921 
1922   /* Get the file system structure from the inode reference. */
1923   if (node->originMount == NULL)
1924     {
1925       return -EISDIR;
1926     }
1927 
1928   fs = node->originMount->data;
1929   if (fs == NULL)
1930     {
1931       return -EISDIR;
1932     }
1933 
1934   DEBUGASSERT(fs != NULL && fs->tfs_root.tde_object != NULL);
1935 
1936   /* Get exclusive access to the file system */
1937 
1938   tmpfs_lock(fs);
1939 
1940   /* Find the file object and parent directory associated with this relative
1941    * path.  If successful, tmpfs_find_file will lock both the file object
1942    * and the parent directory and take one reference count on each.
1943    */
1944 
1945   parent_dir = (struct tmpfs_directory_s *)(parent->data);
1946   if (parent_dir == NULL)
1947     {
1948       ret = tmpfs_find_file(fs, relpath, &tfo, &parent_dir);
1949       if (ret < 0)
1950         {
1951           goto errout_with_lock;
1952         }
1953     }
1954   else
1955     {
1956       tfo = (struct tmpfs_file_s *)node->data;
1957     }
1958 
1959   if (tfo == NULL || parent_dir == NULL)
1960     {
1961       ret = -EISDIR;
1962       goto errout_with_lock;
1963     }
1964   DEBUGASSERT(tfo != NULL);
1965   tmpfs_lock_directory(parent_dir);
1966   tmpfs_lock_file(tfo);
1967 
1968   /* Remove the file from parent directory */
1969   ret = tmpfs_remove_dirent(parent_dir, (struct tmpfs_object_s *)tfo);
1970   if (ret < 0)
1971     {
1972       goto errout_with_objects;
1973     }
1974 
1975   /* If the reference count is not one, then just mark the file as
1976    * unlinked
1977    */
1978 
1979   if (tfo->tfo_refs > 1)
1980     {
1981       /* Make the file object as unlinked */
1982 
1983       tfo->tfo_flags |= TFO_FLAG_UNLINKED;
1984 
1985       /* Release the reference count on the file object */
1986 
1987       tfo->tfo_refs--;
1988       tmpfs_unlock_file(tfo);
1989     }
1990 
1991   /* Otherwise we can free the object now */
1992 
1993   else
1994     {
1995       sem_destroy(&tfo->tfo_exclsem.ts_sem);
1996       kmm_free(tfo->tfo_data);
1997       kmm_free(tfo);
1998       node->data = NULL;
1999     }
2000 
2001   /* Release the reference and lock on the parent directory */
2002 
2003   if (parent_dir->tdo_refs > 0)
2004     {
2005       parent_dir->tdo_refs--;
2006     }
2007   tmpfs_unlock_directory(parent_dir);
2008   tmpfs_unlock(fs);
2009 
2010   return OK;
2011 
2012 errout_with_objects:
2013   tmpfs_release_lockedfile(tfo);
2014 
2015   if (parent_dir->tdo_refs > 0)
2016     {
2017       parent_dir->tdo_refs--;
2018     }
2019 
2020   tmpfs_unlock_directory(parent_dir);
2021 
2022 errout_with_lock:
2023   tmpfs_unlock(fs);
2024   return ret;
2025 }
2026 
2027 /****************************************************************************
2028  * Name: tmpfs_mkdir
2029  ****************************************************************************/
2030 
tmpfs_mkdir(struct Vnode *parent, const char *relpath, mode_t mode, struct Vnode **vpp)2031 int tmpfs_mkdir(struct Vnode *parent, const char *relpath, mode_t mode, struct Vnode **vpp)
2032 {
2033   struct tmpfs_s *fs;
2034   struct Vnode *vp = NULL;
2035   struct tmpfs_directory_s *tdo;
2036   struct tmpfs_directory_s *parent_tdo = NULL;
2037   int ret;
2038 
2039   DEBUGASSERT(parent != NULL && relpath != NULL);
2040 
2041   if (strlen(relpath) == 0)
2042     {
2043       return -EEXIST;
2044     }
2045 
2046   /* Get the file system structure from the inode reference. */
2047 
2048   fs = parent->originMount->data;
2049   DEBUGASSERT(fs != NULL && fs->tfs_root.tde_object != NULL);
2050 
2051   /* Get exclusive access to the file system */
2052 
2053   tmpfs_lock(fs);
2054 
2055   if (parent->data != NULL)
2056     {
2057       parent_tdo = (struct tmpfs_directory_s *)(parent->data);
2058     }
2059   /* Create the directory. */
2060   ret = tmpfs_create_directory(fs, relpath, parent_tdo, &tdo);
2061   if (ret != OK)
2062     {
2063       goto errout_with_lock;
2064     }
2065 
2066   ret = VnodeAlloc(&tmpfs_vops, &vp);
2067   if (ret != 0)
2068     {
2069       goto errout_with_lock;
2070     }
2071 
2072   tdo->mode = mode;
2073   vp->parent = parent;
2074   vp->vop = parent->vop;
2075   vp->fop = parent->fop;
2076   vp->data = tdo;
2077   vp->originMount = parent->originMount;
2078   vp->type = VNODE_TYPE_DIR;
2079   vp->mode = tdo->mode;
2080   vp->gid = tdo->gid;
2081   vp->uid = tdo->uid;
2082 
2083   ret = VfsHashInsert(vp, (uint32_t)tdo);
2084   *vpp = vp;
2085 
2086 errout_with_lock:
2087   tmpfs_unlock(fs);
2088   return ret;
2089 }
2090 
2091 /****************************************************************************
2092  * Name: tmpfs_rmdir
2093  ****************************************************************************/
2094 
tmpfs_rmdir(struct Vnode *parent, struct Vnode *target, const char *dirname)2095 int tmpfs_rmdir(struct Vnode *parent, struct Vnode *target, const char *dirname)
2096 {
2097   struct tmpfs_s *fs;
2098   struct tmpfs_directory_s *parent_dir;
2099   struct tmpfs_directory_s *tdo;
2100   int ret = 0;
2101 
2102   DEBUGASSERT(parent != NULL && target != NULL && dirname != NULL);
2103 
2104   /* Get the file system structure from the inode reference. */
2105   fs = parent->originMount->data;
2106   if (fs == NULL)
2107     {
2108       return -EISDIR;
2109     }
2110   DEBUGASSERT(fs != NULL && fs->tfs_root.tde_object != NULL);
2111 
2112   /* Get exclusive access to the file system */
2113 
2114   tmpfs_lock(fs);
2115 
2116   /* Find the directory object and parent directory associated with this
2117    * relative path.  If successful, tmpfs_find_file will lock both the
2118    * directory object and the parent directory and take one reference count
2119    * on each.
2120    */
2121   parent_dir = (struct tmpfs_directory_s *)(parent->data);
2122   if (parent_dir == NULL)
2123     {
2124       ret = tmpfs_find_directory(fs, dirname, &tdo, &parent_dir);
2125       if (ret < 0)
2126         {
2127           goto errout_with_lock;
2128         }
2129     }
2130   else
2131     {
2132       tdo = (struct tmpfs_directory_s *)target->data;
2133     }
2134 
2135   if (tdo == NULL || tdo->tdo_type != TMPFS_DIRECTORY)
2136     {
2137       ret = -EISDIR;
2138       goto errout_with_lock;
2139     }
2140   tmpfs_lock_directory(parent_dir);
2141 
2142   /* Is the directory empty?  We cannot remove directories that still
2143    * contain references to file system objects.  No can we remove the
2144    * directory if there are outstanding references on it (other than
2145    * our reference).
2146    */
2147 
2148   if (tdo->tdo_nentries > 0 || tdo->tdo_refs > 1)
2149     {
2150       ret = -EBUSY;
2151       goto errout_with_objects;
2152     }
2153   /* Remove the directory from parent directory */
2154   ret = tmpfs_remove_dirent(parent_dir, (struct tmpfs_object_s *)tdo);
2155   if (ret < 0)
2156     {
2157       goto errout_with_objects;
2158     }
2159 
2160   /* Free the directory object */
2161 
2162   sem_destroy(&tdo->tdo_exclsem.ts_sem);
2163   kmm_free(tdo);
2164   target->data = NULL;
2165 
2166   /* Release the reference and lock on the parent directory */
2167 
2168   if (parent_dir->tdo_refs > 0)
2169     {
2170       parent_dir->tdo_refs--;
2171     }
2172 
2173   tmpfs_unlock_directory(parent_dir);
2174   tmpfs_unlock(fs);
2175 
2176   return OK;
2177 
2178 errout_with_objects:
2179   if (tdo->tdo_refs > 0)
2180     {
2181       tdo->tdo_refs--;
2182     }
2183   tmpfs_unlock_directory(tdo);
2184 
2185   if (parent_dir->tdo_refs > 0)
2186     {
2187       parent_dir->tdo_refs--;
2188     }
2189   tmpfs_unlock_directory(parent_dir);
2190 
2191 errout_with_lock:
2192   tmpfs_unlock(fs);
2193   return ret;
2194 }
2195 
2196 /****************************************************************************
2197  * Name: tmpfs_rename
2198  ****************************************************************************/
2199 
tmpfs_rename(struct Vnode *oldVnode, struct Vnode *newParent, const char *oldname, const char *newname)2200 int tmpfs_rename(struct Vnode *oldVnode, struct Vnode *newParent, const char *oldname, const char *newname)
2201 {
2202   struct tmpfs_directory_s *oldparent;
2203   struct tmpfs_directory_s *newparent_tdo;
2204   struct tmpfs_object_s *old_to;
2205   struct tmpfs_dirent_s *tde;
2206   struct tmpfs_directory_s *tdo;
2207   struct tmpfs_file_s *tfo;
2208   struct tmpfs_s *fs;
2209   struct tmpfs_s *new_fs;
2210   struct Vnode *old_parent_vnode;
2211 
2212   char *copy;
2213   int ret = 0;
2214   unsigned int oldrelpath_len, newrelpath_len, cmp_namelen;
2215 
2216   oldrelpath_len = strlen(oldname);
2217   newrelpath_len = strlen(newname);
2218 
2219   cmp_namelen = (oldrelpath_len <= newrelpath_len) ? oldrelpath_len : newrelpath_len;
2220   if (!cmp_namelen || ((!strncmp(oldname, newname, cmp_namelen)) &&
2221            (oldname[cmp_namelen] == '/' ||
2222             newname[cmp_namelen] == '/')))
2223     {
2224       return -EPERM;
2225     }
2226 
2227   /* Get the file system structure from the inode reference. */
2228   if (oldVnode->parent == NULL)
2229     {
2230       return -EPERM;
2231     }
2232 
2233   fs = oldVnode->parent->originMount->data;
2234   if (fs == NULL)
2235     {
2236       return -EPERM;
2237     }
2238 
2239   DEBUGASSERT(fs != NULL && fs->tfs_root.tde_object != NULL);
2240 
2241   /* Duplicate the newpath variable so that we can modify it */
2242 
2243   copy = strdup(newname);
2244   if (copy == NULL)
2245     {
2246       return -ENOSPC;
2247     }
2248 
2249   /* Get exclusive access to the file system */
2250 
2251   tmpfs_lock(fs);
2252 
2253   /* Separate the new path into the new file name and the path to the new
2254    * parent directory.
2255    */
2256   newparent_tdo = (struct tmpfs_directory_s *)(newParent->data);
2257   if (newparent_tdo == NULL)
2258     {
2259       new_fs = newParent->originMount->data;
2260       newparent_tdo = (struct tmpfs_directory_s *)new_fs->tfs_root.tde_object;
2261       if (newparent_tdo == NULL)
2262         {
2263           ret = -ENOTEMPTY;
2264           goto errout_with_lock;
2265         }
2266       tmpfs_lock_directory(newparent_tdo);
2267       newparent_tdo->tdo_refs++;
2268     }
2269 
2270   /* Find the old object at oldpath.  If successful, tmpfs_find_object()
2271    * will lock both the object and the parent directory and will increment
2272    * the reference count on both.
2273    */
2274   old_parent_vnode = oldVnode->parent;
2275   oldparent = (struct tmpfs_directory_s *)(old_parent_vnode->data);
2276   old_to = (struct tmpfs_object_s *)oldVnode->data;
2277 
2278   if (oldparent == NULL)
2279     {
2280       oldparent = (struct tmpfs_directory_s *)fs->tfs_root.tde_object;
2281       if (oldparent == NULL || old_to == NULL)
2282         {
2283           ret = -ENOTEMPTY;
2284           goto errout_with_newparent;
2285         }
2286     }
2287 
2288   tmpfs_lock_directory(oldparent);
2289   tmpfs_lock_object(old_to);
2290   oldparent->tdo_refs++;
2291   old_to->to_refs++;
2292   tde = tmpfs_find_dirent(newparent_tdo, copy);
2293   if (tde != NULL)
2294     {
2295       struct tmpfs_object_s *new_to = tde->tde_object;
2296 
2297       /* Cannot rename a directory to a noempty directory */
2298 
2299       if (tde->tde_object->to_type == TMPFS_DIRECTORY)
2300         {
2301           tdo = (struct tmpfs_directory_s *)new_to;
2302           if (tdo->tdo_nentries != 0)
2303             {
2304               ret = -ENOTEMPTY;
2305               goto errout_with_oldparent;
2306             }
2307         }
2308 
2309       /* Null rename, just return */
2310 
2311       if (old_to == new_to)
2312         {
2313           ret = ENOERR;
2314           goto errout_with_oldparent;
2315         }
2316 
2317       /* Check that we are renaming like-for-like */
2318 
2319       if (old_to->to_type == TMPFS_REGULAR && new_to->to_type == TMPFS_DIRECTORY)
2320         {
2321           ret = -EISDIR;
2322           goto errout_with_oldparent;
2323         }
2324 
2325       if (old_to->to_type == TMPFS_DIRECTORY && new_to->to_type == TMPFS_REGULAR)
2326         {
2327           ret = -ENOTDIR;
2328           goto errout_with_oldparent;
2329         }
2330 
2331       /* Now delete the destination directory entry */
2332 
2333       ret = tmpfs_remove_dirent(newparent_tdo, new_to);
2334       if (ret < 0)
2335         {
2336           goto errout_with_oldparent;
2337         }
2338 
2339       if (new_to->to_type == TMPFS_DIRECTORY)
2340         {
2341           (void)sem_destroy(&new_to->to_exclsem.ts_sem);
2342           kmm_free(new_to);
2343         }
2344       else
2345         {
2346           tfo = (struct tmpfs_file_s *)new_to;
2347           if (new_to->to_refs > 0)
2348             {
2349               /* Make the file object as unlinked */
2350 
2351               tfo->tfo_flags |= TFO_FLAG_UNLINKED;
2352             }
2353 
2354           /* Otherwise we can free the object now */
2355 
2356           else
2357             {
2358               (void)sem_destroy(&tfo->tfo_exclsem.ts_sem);
2359               kmm_free(tfo->tfo_data);
2360               kmm_free(tfo);
2361             }
2362         }
2363     }
2364 
2365   /* Remove the entry from the parent directory */
2366 
2367   ret = tmpfs_remove_dirent(oldparent, old_to);
2368   if (ret < 0)
2369     {
2370       goto errout_with_oldparent;
2371     }
2372 
2373   /* Add an entry to the new parent directory. */
2374 
2375   ret = tmpfs_add_dirent(&newparent_tdo, old_to, copy);
2376   oldVnode->parent = newParent;
2377 
2378 errout_with_oldparent:
2379   if (oldparent == NULL)
2380     {
2381       tmpfs_unlock(fs);
2382       kmm_free(copy);
2383       return ret;
2384     }
2385   if (oldparent->tdo_refs > 0)
2386     {
2387       oldparent->tdo_refs--;
2388     }
2389   tmpfs_unlock_directory(oldparent);
2390   tmpfs_release_lockedobject(old_to);
2391 
2392 errout_with_newparent:
2393   if (newparent_tdo == NULL)
2394     {
2395       tmpfs_unlock(fs);
2396       kmm_free(copy);
2397       return ret;
2398     }
2399   if (newparent_tdo->tdo_refs > 0)
2400     {
2401       newparent_tdo->tdo_refs--;
2402     }
2403   tmpfs_unlock_directory(newparent_tdo);
2404 
2405 errout_with_lock:
2406   tmpfs_unlock(fs);
2407   kmm_free(copy);
2408   return ret;
2409 }
2410 
2411 /****************************************************************************
2412  * Name: tmpfs_stat_common
2413  ****************************************************************************/
2414 
tmpfs_stat_common(struct tmpfs_object_s *to, struct stat *buf)2415 static void tmpfs_stat_common(struct tmpfs_object_s *to,
2416                               struct stat *buf)
2417 {
2418   size_t objsize;
2419 
2420   /* Is the tmpfs object a regular file? */
2421 
2422   (VOID)memset_s(buf, sizeof(struct stat), 0, sizeof(struct stat));
2423 
2424   if (to->to_type == TMPFS_REGULAR)
2425     {
2426       struct tmpfs_file_s *tfo =
2427         (struct tmpfs_file_s *)to;
2428 
2429       /* -rwxrwxrwx */
2430 
2431       buf->st_mode = S_IRWXO | S_IRWXG | S_IRWXU | S_IFREG;
2432       buf->st_nlink = tfo->tfo_refs - 1;
2433 
2434       /* Get the size of the object */
2435 
2436       objsize = tfo->tfo_size;
2437     }
2438   else /* if (to->to_type == TMPFS_DIRECTORY) */
2439     {
2440       struct tmpfs_directory_s *tdo =
2441         (struct tmpfs_directory_s *)to;
2442 
2443       /* drwxrwxrwx */
2444 
2445       buf->st_mode = S_IRWXO | S_IRWXG | S_IRWXU | S_IFDIR;
2446       buf->st_nlink   = tdo->tdo_nentries + tdo->tdo_refs;
2447 
2448       /* Get the size of the object */
2449 
2450       objsize = sizeof(struct tmpfs_directory_s);
2451     }
2452 
2453   /* Fake the rest of the information */
2454 
2455   buf->st_size    = objsize;
2456   buf->st_blksize = 0;
2457   buf->st_blocks  = 0;
2458   buf->st_atime   = to->to_atime;
2459   buf->st_mtime   = to->to_mtime;
2460   buf->st_ctime   = to->to_ctime;
2461 
2462   /* Adapt to kstat member "long tv_sec" */
2463   buf->__st_atim32.tv_sec = (long)to->to_atime;
2464   buf->__st_mtim32.tv_sec = (long)to->to_mtime;
2465   buf->__st_ctim32.tv_sec = (long)to->to_ctime;
2466 
2467 }
2468 
2469 /****************************************************************************
2470  * Name: tmpfs_stat
2471  ****************************************************************************/
2472 
tmpfs_stat(struct Vnode *vp, struct stat *st)2473 int tmpfs_stat(struct Vnode *vp, struct stat *st)
2474 {
2475   struct tmpfs_s *fs;
2476   struct tmpfs_object_s *to;
2477   int ret;
2478 
2479   DEBUGASSERT(vp != NULL && st != NULL);
2480   /* Get the file system structure from the inode reference. */
2481 
2482   fs = vp->originMount->data;
2483   DEBUGASSERT(fs != NULL && fs->tfs_root.tde_object != NULL);
2484 
2485   /* Get exclusive access to the file system */
2486 
2487   tmpfs_lock(fs);
2488 
2489   /* Find the tmpfs object at the relpath.  If successful,
2490    * tmpfs_find_object() will lock the object and increment the
2491    * reference count on the object.
2492    */
2493   if (vp->data != NULL)
2494     {
2495       to = (struct tmpfs_object_s *)vp->data;
2496     }
2497   else
2498     {
2499       to = fs->tfs_root.tde_object;
2500     }
2501   tmpfs_lock_object(to);
2502   to->to_refs++;
2503 
2504   /* We found it... Return information about the file object in the stat
2505    * buffer.
2506    */
2507   if(to == NULL)
2508     {
2509       ret = -ENOENT;
2510       goto errout_with_fslock;
2511     }
2512   DEBUGASSERT(to != NULL);
2513   tmpfs_stat_common(to, st);
2514 
2515   /* Unlock the object and return success */
2516 
2517   tmpfs_release_lockedobject(to);
2518   ret = OK;
2519 
2520 errout_with_fslock:
2521   tmpfs_unlock(fs);
2522   return ret;
2523 }
2524 
2525 FSMAP_ENTRY(ramfs_fsmap, "ramfs", tmpfs_operations, FALSE, FALSE);
2526 
2527 #endif
2528 
2529