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

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