SimpleOS

LXR

Navigation



Site hébergé par : enix

The LXR Cross Referencer for SOS

source navigation ]
diff markup ]
identifier search ]
general search ]
 
 
Article:1 ] [ 2 ] [ 3 ] [ 4 ] [ 5 ] [ 6 ] [ 6.5 ] [ 7 ] [ 7.5 ] [ 8 ] [ 9 ] [ 9.5 ]

001 /* Copyright (C) 2005 David Decotigny
002 
003    This program is free software; you can redistribute it and/or
004    modify it under the terms of the GNU General Public License
005    as published by the Free Software Foundation; either version 2
006    of the License, or (at your option) any later version.
007    
008    This program is distributed in the hope that it will be useful,
009    but WITHOUT ANY WARRANTY; without even the implied warranty of
010    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
011    GNU General Public License for more details.
012    
013    You should have received a copy of the GNU General Public License
014    along with this program; if not, write to the Free Software
015    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
016    USA. 
017 */
018 
019 #include <sos/kmalloc.h>
020 #include <sos/klibc.h>
021 #include <sos/assert.h>
022 #include <sos/list.h>
023 #include <sos/ksynch.h>
024 #include <sos/uaccess.h>
025 #include <sos/physmem.h>
026 
027 #include <sos/fs.h>
028 #include <sos/fs_nscache.h>
029 
030 #include "fs_virtfs.h"
031 
032 /**
033  * @file fs_virtfs.c
034  *
035  * All the sos_fs_nodes and their contents are stored in kernel
036  * memory. Thus, with "virtfs", the "disk" is actually the kernel
037  * memory. In a sos_fs_node, the storage_location field corresponds to
038  * the kernel address of the corresponding struct sos_fs_node.
039  *
040  * Basic kernel-memory virtual FS. Highly UNDER-optimized filesystem:
041  * each time we resize a file, a complete reallocation is done. This
042  * somehow complicates the file mapping, as we cannot do this anyhow
043  * as long as the file is mapped because the kernel address of the
044  * file contents must NOT change. For that reason, we forbid to resize
045  * a file as soon as it is mapped and the file's contents are aligned
046  * on a page boundary in kernel memory (use of sos_kmem_vmm_alloc
047  * instead of sos_kmalloc).
048  */
049 
050 
051 /**
052  * A directory entry, used in virtfs_node of type "directory". This is
053  * as simple as a sos_fs_node with a name.
054  */
055 struct virtfs_direntry
056 {
057   char * name;
058   struct sos_fs_node * fsnode;
059 
060   /**
061    * Used by readdir to be resilient against creat/mkdir/unlink/rmdir
062    * between 2 successive readdirs. Each time a new child to a node is
063    * allocated, a new "creation_order" is uniquely attributed. 64bits
064    * should be large enough.
065    */
066   sos_lcount_t creation_order;
067 
068   struct virtfs_direntry * sibling_prev, * sibling_next;
069 };
070 
071 
072 /**
073  * Structure of a FS file or dir for virtfs. Virtfs only supports
074  * regular files, directories or symbolic links:
075  *  - regular files correspond to an area in kernel memory
076  *  - directories correspond to a list of virtfs_direntry
077  *
078  * Structural inheritance of sos_fs_node: "super" is the ancestor (in
079  * "OOP" terms). The "super" field might not be the first field: from
080  * a sos_fs_node, the corresponding virtfs_node is given by the
081  * sos_fs_node::custom_data field
082  */
083 struct virtfs_node
084 {
085   /** The ancestor */
086   struct sos_fs_node super;
087 
088   union
089   {
090     /* A file */
091     struct
092     {
093       /* Contents of the file */
094       sos_size_t size;
095       void * data;
096 
097       /* Yes, the file can be mapped ! */
098       struct sos_umem_vmm_mapped_resource mapres;
099       sos_count_t num_mappings; /**< Forbids the region to be resized
100                                    while it's being mapped, because this
101                                    woould cause the underlying data area
102                                    to be moved */
103     } file;
104 
105     /* A directory */
106     struct
107     {
108       /** the children nodes are inserted in FIFO order. This is
109           important for the readdir function to be resilient against
110           mkdir/rmdir/creat/unlink */
111       struct virtfs_direntry * list_entries;
112 
113       /** Used by readdir to remember the last unique "creation order"
114           attributed */
115       sos_lcount_t top_creation_order;
116     } dir;
117   }; /* Anonymous union (gcc extension) */
118 
119   /* The virtfs nodes are chained */
120   struct virtfs_node * prev, * next;
121 };
122 
123 
124 /**
125  * A virtfs FS instance
126  *
127  * Structural inheritance of sos_fs_manager_instance: "super" is the
128  * ancestor (in "OOP" terms). The "super" field might not be the first
129  * field: from a sos_fs_manager_instance, the corresponding
130  * virtfs_instance is given by the
131  * sos_fs_manager_instance::custom_data field
132  */
133 struct virtfs_instance
134 {
135   /** Ancestor */
136   struct sos_fs_manager_instance super;
137 
138   /** For the serialization of virtfs "disk" (ie kernel memory)
139       accesses */
140   struct sos_kmutex lock;
141 
142   /** The list of virtfs nodes "on disk" (ie in kernel memory) */
143   struct virtfs_node * list_fsnodes;
144 };
145 
146 
147 /** The description of the "Virtual FS" */
148 static struct sos_fs_manager_type virtfs_type;
149 
150 
151 /* ********** Forward declarations */
152 static sos_ret_t virtfs_mount(struct sos_fs_manager_type * this,
153                               struct sos_fs_node * device,
154                               const char * args,
155                               struct sos_fs_manager_instance ** mounted_fs);
156 
157 
158 static sos_ret_t virtfs_umount(struct sos_fs_manager_type * this,
159                                struct sos_fs_manager_instance * mounted_fs);
160 
161 
162 static sos_ret_t virtfs_new_mapping(struct sos_umem_vmm_vr *vr);
163 
164 
165 sos_ret_t sos_fs_virtfs_subsystem_setup()
166 {
167   strzcpy(virtfs_type.name, "virtfs", SOS_FS_MANAGER_NAME_MAXLEN);
168   virtfs_type.mount  = virtfs_mount;
169   virtfs_type.umount = virtfs_umount;
170 
171   return sos_fs_register_fs_type(& virtfs_type);
172 }
173 
174 
175 /* ********************************************************
176  * Helper functions
177  */
178 
179 
180 /* Helper function to serialize "disk" accesses */
181 inline static void virtfs_lock(struct virtfs_node *a_node)
182 {
183   struct virtfs_instance * fs = (struct virtfs_instance*)a_node->super.fs->custom_data;
184   sos_kmutex_lock(& fs->lock, NULL);
185 }
186 
187 
188 /* Helper function to serialize "disk" accesses */
189 inline static void virtfs_unlock(struct virtfs_node *a_node)
190 {
191   struct virtfs_instance * fs = (struct virtfs_instance*)a_node->super.fs->custom_data;
192   sos_kmutex_unlock(& fs->lock);
193 }
194 
195 
196 /* Helper function to resize the given virts node (ie kernel memory
197    reallocation) */
198 static sos_ret_t virtfs_resize(struct virtfs_node *this,
199                                sos_size_t new_size)
200 {
201   void * new_data = NULL;
202 
203   if (this->file.size == new_size)
204     return SOS_OK;
205 
206   /* Don't allow to resize the region when the file is being mapped */
207   if (this->file.num_mappings > 0)
208     return -SOS_EBUSY;
209 
210   if (new_size > 0)
211     {
212       /* Use kmem_vmm_alloc instead of kmalloc to make sure the data
213          WILL be page-aligned (needed by file mapping stuff) */
214       sos_ui32_t npages = SOS_PAGE_ALIGN_SUP(new_size) / SOS_PAGE_SIZE;
215       new_data = (void*)sos_kmem_vmm_alloc(npages,
216                                            SOS_KMEM_VMM_MAP);
217       if (! new_data)
218         return -SOS_OK;
219     }
220 
221   /* Copy the data to its new location */
222   if (this->file.size < new_size)
223     {
224       if (this->file.size > 0)
225         memcpy(new_data, this->file.data, this->file.size);
226       if (new_size > this->file.size)
227         memset(new_data + this->file.size, 0x0,
228                new_size - this->file.size);
229     }
230   else if (new_size > 0)
231     memcpy(new_data, this->file.data, new_size);
232 
233   if (this->file.data)
234     sos_kfree((sos_vaddr_t)this->file.data);
235   this->file.data = new_data;
236   this->file.size = new_size;
237 
238   return SOS_OK;
239 }
240 
241 
242 /* ********************************************************
243  * Opened file operations
244  */
245 
246 
247 static sos_ret_t
248 virtfs_duplicate_opened_file(struct sos_fs_opened_file *this,
249                              const struct sos_process  * for_owner,
250                              struct sos_fs_opened_file **result)
251 {
252   *result = (struct sos_fs_opened_file*)
253     sos_kmalloc(sizeof(struct sos_fs_opened_file), 0);
254   if (! *result)
255     return -SOS_ENOMEM;
256 
257   memcpy(*result, this, sizeof(*this));
258   (*result)->owner = for_owner;
259   return SOS_OK;
260 }
261 
262 
263 static sos_ret_t
264 virtfs_seek(struct sos_fs_opened_file *this,
265             sos_lsoffset_t offset,
266             sos_seek_whence_t whence,
267             /* out */ sos_lsoffset_t * result_position)
268 {
269   sos_lsoffset_t ref_offs;
270   struct virtfs_node * virtfsnode;
271 
272   virtfsnode = (struct virtfs_node*)
273     sos_fs_nscache_get_fs_node(this->direntry)->custom_data;
274 
275   if ( (virtfsnode->super.type != SOS_FS_NODE_REGULAR_FILE)
276        && (virtfsnode->super.type != SOS_FS_NODE_SYMLINK))
277     return -SOS_ENOSUP;
278 
279   *result_position = this->position;
280   switch (whence)
281     {
282     case SOS_SEEK_SET:
283       ref_offs = 0;
284       break;
285 
286     case SOS_SEEK_CUR:
287       ref_offs = this->position;
288       break;
289 
290     case SOS_SEEK_END:
291       ref_offs = virtfsnode->file.size;
292       break;
293 
294     default:
295       return -SOS_EINVAL;
296     }
297 
298   if (offset < -ref_offs)
299     return -SOS_EINVAL;
300   
301   this->position = ref_offs + offset;
302   *result_position = this->position;
303   return SOS_OK;
304 }
305 
306 
307 static sos_ret_t virtfs_read(struct sos_fs_opened_file *this,
308                              sos_uaddr_t dest_buf,
309                              sos_size_t * /* in/out */len)
310 {
311   sos_ret_t retval;
312   struct virtfs_node * virtfsnode;
313 
314   virtfsnode = (struct virtfs_node*)
315     sos_fs_nscache_get_fs_node(this->direntry)->custom_data;
316 
317   if ( (virtfsnode->super.type != SOS_FS_NODE_REGULAR_FILE)
318        && (virtfsnode->super.type != SOS_FS_NODE_SYMLINK))
319     return -SOS_ENOSUP;
320 
321   if (this->position >= virtfsnode->file.size)
322     {
323       *len = 0;
324       return SOS_OK;
325     }
326 
327   virtfs_lock(virtfsnode);
328 
329   if (this->position + *len >= virtfsnode->file.size)
330     *len = virtfsnode->file.size - this->position;
331 
332   retval = sos_memcpy_to_user(dest_buf,
333                               ((sos_vaddr_t)virtfsnode->file.data)
334                               + this->position,
335                               *len);
336   if (retval < 0)
337     {
338       virtfs_unlock(virtfsnode);
339       return retval;
340     }
341 
342   this->position += retval;
343   *len = retval;
344 
345   virtfs_unlock(virtfsnode);
346 
347   return SOS_OK;
348 }
349 
350 
351 static sos_ret_t virtfs_write(struct sos_fs_opened_file *this,
352                              sos_uaddr_t src_buf,
353                              sos_size_t * /* in/out */len)
354 {
355   sos_ret_t retval;
356   struct virtfs_node * virtfsnode;
357 
358   virtfsnode = (struct virtfs_node*)
359     sos_fs_nscache_get_fs_node(this->direntry)->custom_data;
360 
361   if ( (virtfsnode->super.type != SOS_FS_NODE_REGULAR_FILE)
362        && (virtfsnode->super.type != SOS_FS_NODE_SYMLINK))
363     return -SOS_ENOSUP;
364 
365   virtfs_lock(virtfsnode);
366   if (this->position + *len >= virtfsnode->file.size)
367     {
368       /* Try to resize if needed */
369       if (SOS_OK != virtfs_resize(virtfsnode, this->position + *len))
370         *len = virtfsnode->file.size - this->position;
371     }
372 
373   retval = sos_memcpy_from_user(((sos_vaddr_t)virtfsnode->file.data)
374                                   + this->position,
375                                 src_buf,
376                                 *len);
377   if (retval < 0)
378     {
379       virtfs_unlock(virtfsnode);
380       return retval;
381     }
382 
383   this->position += retval;
384   *len = retval;
385 
386   virtfs_unlock(virtfsnode);
387 
388   return SOS_OK;
389 }
390 
391 
392 static sos_ret_t virtfs_mmap(struct sos_fs_opened_file *this,
393                              sos_uaddr_t *uaddr, sos_size_t size,
394                              sos_ui32_t access_rights,
395                              sos_ui32_t flags,
396                              sos_luoffset_t offset)
397 {
398   struct virtfs_node * virtfsnode;
399 
400   virtfsnode = (struct virtfs_node*)
401     sos_fs_nscache_get_fs_node(this->direntry)->custom_data;
402 
403   if (virtfsnode->super.type != SOS_FS_NODE_REGULAR_FILE)
404     return -SOS_ENOSUP;
405 
406   return sos_umem_vmm_map(sos_process_get_address_space(this->owner),
407                           uaddr, size, access_rights,
408                           flags, & virtfsnode->file.mapres, offset);
409 }
410 
411 
412 static sos_ret_t virtfs_readdir(struct sos_fs_opened_file *this,
413                                 struct sos_fs_dirent * result)
414 {
415   /* For directories, "position" indicates the "creation_order" of the
416      last readdir operation, and "custom_data" indicates the address
417      of the last directory entry */
418 
419   struct virtfs_direntry * direntry, * next_direntry;
420   struct virtfs_node * virtfsnode;
421   int nb;
422 
423   virtfsnode = (struct virtfs_node*)
424     sos_fs_nscache_get_fs_node(this->direntry)->custom_data;
425   next_direntry = NULL;
426 
427   /* If the "generation" of the node did not change, the next direntry
428      is the next in the list. We can safely do this because the list
429      of direntries is sorted in increasing creation_order. */
430   if ((this->generation == virtfsnode->super.generation)
431       && (this->custom_data != NULL))
432     {
433       direntry = (struct virtfs_direntry*)this->custom_data;
434       next_direntry = direntry->sibling_next;
435 
436       /* Did we go past the end of the list ? */
437       if (next_direntry == list_get_head_named(virtfsnode->dir.list_entries,
438                                                sibling_prev, sibling_next))
439         next_direntry = NULL;
440     }
441   else
442     /* Otherwise we have to lookup the next entry manually */
443     {
444       /* Lookup the entry that has next creation_order */
445       next_direntry = NULL;
446       list_foreach_forward_named(virtfsnode->dir.list_entries,
447                                  direntry, nb,
448                                  sibling_prev, sibling_next)
449         {
450           if (direntry->creation_order <= this->position)
451             continue;
452 
453           if (! next_direntry)
454             {
455               next_direntry = direntry;
456               continue;
457             }
458           
459           if (direntry->creation_order < next_direntry->creation_order)
460             next_direntry = direntry;
461         }
462     }
463 
464   if (! next_direntry)
465     {
466       this->custom_data = NULL;
467       this->position    = 0;
468       return -SOS_ENOENT;
469     }
470 
471   /* Update the result */
472   result->storage_location  = ((sos_vaddr_t)next_direntry->fsnode);
473   result->offset_in_dirfile = next_direntry->creation_order;
474   result->type              = next_direntry->fsnode->type;
475   result->namelen           = strnlen(next_direntry->name,
476                                       SOS_FS_DIRENT_NAME_MAXLEN);
477   strzcpy(result->name, next_direntry->name, SOS_FS_DIRENT_NAME_MAXLEN);
478 
479   /* Update the custom data */
480   this->position    = next_direntry->creation_order;
481   this->custom_data = next_direntry;
482 
483   return SOS_OK;
484 }
485 
486 
487 static struct sos_fs_ops_opened_file virtfs_ops_opened_file =
488   (struct sos_fs_ops_opened_file){
489     .seek     = virtfs_seek,
490     .read     = virtfs_read,
491     .write    = virtfs_write,
492     .mmap     = virtfs_mmap
493   };
494 
495 
496 static struct sos_fs_ops_opened_dir virtfs_ops_opened_dir =
497   (struct sos_fs_ops_opened_dir){
498     .readdir  = virtfs_readdir
499   };
500 
501 
502 /* ********************************************************
503  * FS node operations
504  */
505 
506 static sos_ret_t virtfs_stat_node(struct sos_fs_node * this,
507                                   struct sos_fs_stat * result)
508 {
509   struct virtfs_node * virtfsnode = (struct virtfs_node*)this->custom_data;
510   result->st_type             = this->type;
511   result->st_storage_location = this->storage_location;
512   result->st_access_rights    = this->access_rights;
513   result->st_nlink            = this->ondisk_lnk_cnt;
514   if (this->type == SOS_FS_NODE_REGULAR_FILE)
515     result->st_size           = virtfsnode->file.size;
516   else
517     result->st_size           = 0;
518   return SOS_OK;
519 }
520 
521 
522 static sos_ret_t virtfs_truncate(struct sos_fs_node *this,
523                                  sos_lsoffset_t length)
524 {
525   sos_ret_t retval;
526   struct virtfs_node * virtfsnode;
527 
528   virtfsnode = (struct virtfs_node*) this->custom_data;
529 
530   if ( (virtfsnode->super.type != SOS_FS_NODE_REGULAR_FILE)
531        && (virtfsnode->super.type != SOS_FS_NODE_SYMLINK))
532     return -SOS_ENOSUP;
533   
534   virtfs_lock(virtfsnode);
535   retval = virtfs_resize(virtfsnode, length);
536   virtfs_unlock(virtfsnode);
537 
538   return retval;
539 }
540 
541 
542 static sos_ret_t virtfs_sync_node(struct sos_fs_node *this)
543 {
544   /* No backing store for virtfs */
545   return SOS_OK;
546 }
547 
548 
549 static sos_ret_t virtfs_chmod_node(struct sos_fs_node * this,
550                                    sos_ui32_t access_rights)
551 {
552   this->access_rights = access_rights;
553   return SOS_OK;
554 }
555 
556 
557 /** Callback when nothing (in particular no sos_fs_nscache_node) make
558     reference to the node */
559 static sos_ret_t virtfs_node_destructor(struct sos_fs_node * this)
560 {
561   /* This callback is called only when the fsnode is not needed
562      anymore by any process or by the kernel. But the node must remain
563      stored "on disk" as long as the FS is mounted AND the node is
564      still "on disk" */
565   if (this->ondisk_lnk_cnt <= 0)
566     {
567       struct virtfs_instance * virtfs = (struct virtfs_instance*)this->fs->custom_data;
568       struct virtfs_node * virtfsnode = (struct virtfs_node*)this->custom_data;
569 
570       list_delete(virtfs->list_fsnodes, virtfsnode);
571       sos_kfree((sos_vaddr_t) this->custom_data);
572     }
573 
574   return SOS_OK;
575 }
576 
577 
578 static struct sos_fs_node_ops_file virtfs_ops_file =
579   (struct sos_fs_node_ops_file){
580     .truncate = virtfs_truncate,
581     .stat     = virtfs_stat_node,
582     .sync     = virtfs_sync_node,
583     .chmod    = virtfs_chmod_node
584   };
585 
586 
587 static sos_ret_t virtfs_new_opened_file(struct sos_fs_node * this,
588                                         const struct sos_process * owner,
589                                         sos_ui32_t open_flags,
590                                         struct sos_fs_opened_file ** result_of)
591 {
592   struct sos_fs_opened_file * of
593     = (struct sos_fs_opened_file*)sos_kmalloc(sizeof(*of), 0);
594   if (! of)
595     return -SOS_ENOMEM;
596 
597   memset(of, 0x0, sizeof(*of));
598   of->owner      = owner;
599   of->duplicate  = virtfs_duplicate_opened_file;
600   of->open_flags = open_flags;
601   of->ops_file   = & virtfs_ops_opened_file;
602   if (this->type == SOS_FS_NODE_DIRECTORY)
603     of->ops_dir  = & virtfs_ops_opened_dir;
604 
605   *result_of = of;
606   return SOS_OK;
607 }
608 
609 
610 static sos_ret_t virtfs_close_opened_file(struct sos_fs_node * this,
611                                           struct sos_fs_opened_file * of)
612 {
613   sos_kfree((sos_vaddr_t)of);
614   return SOS_OK;
615 }
616 
617 
618 static sos_ret_t virtfs_symlink_expand(struct sos_fs_node *this,
619                                        char const  ** target,
620                                        sos_size_t * target_len)
621 {
622   struct virtfs_node * virtfsnode = (struct virtfs_node*)this->custom_data;
623   
624   *target = virtfsnode->file.data;
625   *target_len = virtfsnode->file.size;
626 
627   return SOS_OK;
628 }
629 
630 
631 static struct sos_fs_node_ops_symlink virtfs_ops_symlink
632   = (struct sos_fs_node_ops_symlink){
633     .expand = virtfs_symlink_expand
634   };
635 
636 
637 static sos_ret_t virtfs_dir_lookup(struct sos_fs_node *this,
638                                    const char * name, sos_ui16_t namelen,
639                                    sos_ui64_t * result_storage_location)
640 {
641   struct virtfs_node * virtfsnode = (struct virtfs_node*)this->custom_data;
642   struct virtfs_direntry * direntry;
643   int nbentries;
644   
645   list_foreach_forward_named(virtfsnode->dir.list_entries,
646                              direntry, nbentries,
647                              sibling_prev, sibling_next)
648     {
649       if (!memcmp(name, direntry->name, namelen) && !direntry->name[namelen])
650         {
651           *result_storage_location = direntry->fsnode->storage_location;
652           return SOS_OK;
653         }
654     }
655 
656   return -SOS_ENOENT;
657 }
658 
659 
660 static sos_ret_t virtfs_link(struct sos_fs_node *this,
661                              const struct sos_process *actor,
662                              const char * entry_name, sos_ui16_t entry_namelen,
663                              struct sos_fs_node * node)
664 {
665   struct virtfs_node * parent = (struct virtfs_node*)this->custom_data;
666   struct virtfs_direntry * direntry;
667 
668   direntry = (struct virtfs_direntry*)sos_kmalloc(sizeof(*direntry), 0);
669   if (! direntry)
670     return -SOS_ENOMEM;
671 
672   direntry->name = (char*)sos_kmalloc(entry_namelen + 1, 0);
673   if (! direntry->name)
674     {
675       sos_kfree((sos_vaddr_t)direntry->name);
676       return -SOS_ENOMEM;
677     }
678 
679   memcpy(direntry->name, entry_name, entry_namelen);
680   direntry->name[entry_namelen] = '\0';
681 
682   direntry->fsnode = node;
683   node->ondisk_lnk_cnt ++;
684   this->ondisk_lnk_cnt ++;
685   list_add_tail_named(parent->dir.list_entries, direntry,
686                       sibling_prev, sibling_next);
687 
688   /* Update the index of the new entry in order for the next readdirs
689      to fetch this new entry */
690   parent->dir.top_creation_order ++;
691   direntry->creation_order = parent->dir.top_creation_order;
692 
693   return SOS_OK;
694 }
695 
696 
697 static sos_ret_t
698 virtfs_unlink(struct sos_fs_node *this,
699               const struct sos_process *actor,
700               const char * entry_name, sos_ui16_t entry_namelen)
701 {
702   struct virtfs_node * parent = (struct virtfs_node*)this->custom_data;
703   struct virtfs_direntry * direntry;
704   int nbentries;
705   
706   list_foreach_forward_named(parent->dir.list_entries,
707                              direntry, nbentries,
708                              sibling_prev, sibling_next)
709     {
710       if (!memcmp(entry_name, direntry->name, entry_namelen)
711           && !direntry->name[entry_namelen])
712         {
713           list_delete_named(parent->dir.list_entries, direntry,
714                             sibling_prev, sibling_next);
715           direntry->fsnode->ondisk_lnk_cnt --;
716           this->ondisk_lnk_cnt --;
717           sos_kfree((sos_vaddr_t)direntry);
718           return SOS_OK;
719         }
720     }
721 
722   return -SOS_ENOENT;
723 }
724 
725 
726 static struct sos_fs_node_ops_dir virtfs_ops_dir
727   = (struct sos_fs_node_ops_dir){
728     .lookup = virtfs_dir_lookup,
729     .link   = virtfs_link,
730     .unlink = virtfs_unlink
731   };
732 
733 
734 /* ********************************************************
735  * FS instance operations
736  */
737 
738 
739 /** Simulate the access to a node located on disk. In virtfs, the
740     "disk" is directly the kernel memory, so the
741     "sos_fs_node::storage_location field corresponds to a kernel
742     address */
743 static sos_ret_t
744 virtfs_fetch_node_from_disk(struct sos_fs_manager_instance * this,
745                             sos_ui64_t storage_location,
746                             struct sos_fs_node ** result)
747 {
748   /* The "disk" is simply the ram */
749   struct virtfs_node * virtfsnode;
750 
751   virtfsnode = (struct virtfs_node *)((sos_vaddr_t)storage_location);
752   *result = & virtfsnode->super;
753 
754   return SOS_OK;
755 }
756 
757 
758 static sos_ret_t
759 virtfs_allocate_new_node(struct sos_fs_manager_instance * this,
760                          sos_fs_node_type_t type,
761                          const struct sos_process * creator,
762                          sos_ui32_t access_rights,
763                          sos_ui32_t flags,
764                          struct sos_fs_node ** result)
765 {
766   struct virtfs_node * virtfsnode;
767 
768   /* Allow only DIRs or FILEs */
769   if ((type != SOS_FS_NODE_REGULAR_FILE)
770       && (type != SOS_FS_NODE_SYMLINK)
771       && (type != SOS_FS_NODE_DIRECTORY))
772     return -SOS_ENOSUP;
773 
774   virtfsnode = (struct virtfs_node*) sos_kmalloc(sizeof(*virtfsnode), 0);
775   if (! virtfsnode)
776     return -SOS_ENOMEM;
777 
778   memset(virtfsnode, 0x0, sizeof(*virtfsnode));
779   *result = & virtfsnode->super;
780 
781   /* Initialize "file" */
782   (*result)->inmem_ref_cnt     = 1;
783   (*result)->custom_data       = virtfsnode;
784   (*result)->storage_location  = (sos_ui64_t)((sos_vaddr_t)virtfsnode);
785   (*result)->type              = type;
786   (*result)->access_rights     = access_rights;
787   (*result)->destructor        = virtfs_node_destructor;
788   (*result)->new_opened_file   = virtfs_new_opened_file;
789   (*result)->close_opened_file = virtfs_close_opened_file;
790   (*result)->ops_file          = & virtfs_ops_file;
791   if (type == SOS_FS_NODE_SYMLINK)
792     (*result)->ops_symlink = & virtfs_ops_symlink;
793   else if (type == SOS_FS_NODE_DIRECTORY)
794     (*result)->ops_dir = & virtfs_ops_dir;
795 
796   /* Initialize mapping structure */
797   if (type == SOS_FS_NODE_REGULAR_FILE)
798     {
799       virtfsnode->file.mapres.allowed_access_rights 
800         = SOS_VM_MAP_PROT_READ
801         | SOS_VM_MAP_PROT_WRITE
802         | SOS_VM_MAP_PROT_EXEC;
803       virtfsnode->file.mapres.custom_data    = virtfsnode;
804       virtfsnode->file.mapres.mmap           = virtfs_new_mapping;
805     }
806 
807   list_add_tail(((struct virtfs_instance*)this->custom_data)->list_fsnodes,
808                 virtfsnode);
809 
810   return SOS_OK;
811 }
812 
813 
814 /* ********************************************************
815  * FS type (mount/umount) operations
816  */
817 
818 static sos_ret_t virtfs_mount(struct sos_fs_manager_type * this,
819                               struct sos_fs_node * device,
820                               const char * args,
821                               struct sos_fs_manager_instance ** mounted_fs)
822 {
823   sos_ret_t retval;
824   struct virtfs_instance * fs;
825   struct sos_fs_node * fsnode_root;
826   struct sos_hash_table * hash;
827 
828   *mounted_fs = (struct sos_fs_manager_instance*)NULL;
829 
830   /* Create FS node hash table */
831   hash = sos_hash_create("virtfs H", struct sos_fs_node,
832                          sos_hash_ui64,
833                          sos_hash_key_eq_ui64, 17,
834                          storage_location, hlink_nodecache);
835   if (! hash)
836     return -SOS_ENOMEM;
837 
838   fs = (struct virtfs_instance*)
839     sos_kmalloc(sizeof(struct virtfs_instance), 0);
840   if (! fs)
841     {
842       sos_hash_dispose(hash);
843       return -SOS_ENOMEM;
844     }
845 
846   memset(fs, 0x0, sizeof(struct virtfs_instance));
847   retval = sos_kmutex_init(& fs->lock, "virtfs", SOS_KWQ_ORDER_FIFO);
848   if (SOS_OK != retval)
849     {
850       sos_hash_dispose(hash);
851       sos_kfree((sos_vaddr_t) fs);
852       return retval;
853     }
854   fs->super.custom_data          = fs;
855   fs->super.fs_type              = this;
856   fs->super.allocate_new_node    = virtfs_allocate_new_node;
857   fs->super.fetch_node_from_disk = virtfs_fetch_node_from_disk;
858   fs->super.nodecache            = hash;
859 
860   retval = virtfs_allocate_new_node(& fs->super, SOS_FS_NODE_DIRECTORY,
861                                     NULL,
862                                     SOS_FS_READABLE | SOS_FS_WRITABLE
863                                     | SOS_FS_EXECUTABLE,
864                                     0,
865                                     & fsnode_root);
866   if (SOS_OK != retval)
867     {
868       sos_hash_dispose(hash);
869       sos_kmutex_dispose(& fs->lock);
870       sos_kfree((sos_vaddr_t) fs);
871       return retval;
872     }
873 
874   retval = sos_fs_register_fs_instance(& fs->super, fsnode_root);
875   sos_fs_unref_fsnode(fsnode_root);
876   if (SOS_OK != retval)
877     {
878       sos_hash_dispose(hash);
879       sos_kmutex_dispose(& fs->lock);
880       sos_kfree((sos_vaddr_t) fs);
881       return retval;
882     }
883 
884   *mounted_fs = & fs->super;
885   return SOS_OK;
886 }
887 
888 
889 static sos_ret_t virtfs_umount(struct sos_fs_manager_type * this,
890                                struct sos_fs_manager_instance * mounted_fs)
891 {
892   struct virtfs_instance * virtfs = (struct virtfs_instance*)mounted_fs->custom_data;
893 
894   sos_hash_dispose(virtfs->super.nodecache);
895   while (! list_is_empty(virtfs->list_fsnodes))
896     {
897       struct virtfs_node * virtfsnode = list_pop_head(virtfs->list_fsnodes);
898       
899       if (virtfsnode->super.type == SOS_FS_NODE_REGULAR_FILE)
900         {
901           if (virtfsnode->file.size > 0)
902             sos_kfree((sos_vaddr_t) virtfsnode->file.data);
903         }
904       else if (virtfsnode->super.type == SOS_FS_NODE_DIRECTORY)
905         {
906           while (! list_is_empty_named(virtfsnode->dir.list_entries,
907                                        sibling_prev, sibling_next))
908             {
909               struct virtfs_direntry * direntry
910                 = list_pop_head_named(virtfsnode->dir.list_entries,
911                                       sibling_prev, sibling_next);
912               
913               sos_kfree((sos_vaddr_t)direntry->name);
914               sos_kfree((sos_vaddr_t)direntry);
915             }
916         }
917 
918       sos_kfree((sos_vaddr_t)virtfsnode);
919     }
920 
921   sos_fs_unregister_fs_instance(& virtfs->super);
922   sos_kmutex_dispose(& virtfs->lock);
923   sos_kfree((sos_vaddr_t)virtfs);
924   return SOS_OK;
925 }
926 
927 
928 /* ********************************************************
929  * File mapping stuff
930  */
931 inline static struct virtfs_node *
932 get_virtfsnode_of_vr(struct sos_umem_vmm_vr * vr)
933 {
934   struct sos_umem_vmm_mapped_resource *mr
935     = sos_umem_vmm_get_mapped_resource_of_vr(vr);
936 
937   return (struct virtfs_node *)mr->custom_data;
938 }
939 
940 
941 static void virtfs_map_ref(struct sos_umem_vmm_vr * vr)
942 {
943   struct virtfs_node * virtfsnode = get_virtfsnode_of_vr(vr);
944   sos_fs_ref_fsnode(& virtfsnode->super);
945   virtfsnode->file.num_mappings ++;
946 }
947 
948 
949 static void virtfs_map_unref(struct sos_umem_vmm_vr * vr)
950 {
951   struct virtfs_node * virtfsnode = get_virtfsnode_of_vr(vr);
952 
953   SOS_ASSERT_FATAL(virtfsnode->file.num_mappings > 0);
954   virtfsnode->file.num_mappings --;
955 
956   _sos_fs_unref_fsnode(& virtfsnode->super);
957 }
958 
959 
960 static sos_ret_t virtfs_map_page_in(struct sos_umem_vmm_vr * vr,
961                                     sos_uaddr_t uaddr,
962                                     sos_bool_t write_access)
963 {
964   struct virtfs_node * virtfsnode = get_virtfsnode_of_vr(vr);
965   sos_luoffset_t offset = uaddr - sos_umem_vmm_get_start_of_vr(vr);
966   sos_ret_t retval = SOS_OK;
967   sos_paddr_t ppage_paddr;
968 
969   /* The region is not allowed to be resized */
970   if (SOS_PAGE_ALIGN_SUP(offset) > virtfsnode->file.size)
971     return -SOS_EFAULT;
972 
973   /* Lookup physical kernel page */
974   ppage_paddr = sos_paging_get_paddr(SOS_PAGE_ALIGN_INF(virtfsnode->file.data
975                                                         + offset));
976 
977   /* Cannot access unmapped kernel pages */
978   if (! ppage_paddr)
979     return -SOS_EFAULT;
980   
981   /* Remap it in user space */
982   retval = sos_paging_map(ppage_paddr,
983                           SOS_PAGE_ALIGN_INF(uaddr),
984                           TRUE,
985                           sos_umem_vmm_get_prot_of_vr(vr));
986 
987   return retval;
988 }
989 
990 
991 static struct sos_umem_vmm_vr_ops virtfs_map_ops
992   = (struct sos_umem_vmm_vr_ops){
993     .ref     = virtfs_map_ref,
994     .unref   = virtfs_map_unref,
995     .page_in = virtfs_map_page_in
996   };
997 
998 static sos_ret_t virtfs_new_mapping(struct sos_umem_vmm_vr *vr)
999 {
1000   struct virtfs_node * virtfsnode = get_virtfsnode_of_vr(vr);
1001   sos_size_t reqsize;
1002   sos_ret_t retval;
1003 
1004   reqsize  = sos_umem_vmm_get_offset_in_resource(vr);
1005   reqsize += sos_umem_vmm_get_size_of_vr(vr);
1006 
1007   /* Resize the region NOW */
1008   if (reqsize > virtfsnode->file.size)
1009     {
1010       retval = virtfs_resize(virtfsnode,
1011                              SOS_PAGE_ALIGN_SUP(reqsize));
1012       if (SOS_OK != retval)
1013         return retval;
1014     }
1015 
1016   return sos_umem_vmm_set_ops_of_vr(vr, &virtfs_map_ops);
1017 }

source navigation ] diff markup ] identifier search ] general search ]