source: trunk/kernel/vfs/vfs.c @ 27

Last change on this file since 27 was 23, checked in by alain, 7 years ago

Introduce syscalls.

File size: 43.3 KB
RevLine 
[1]1/*
2 * vfs.c - Virtual File System implementation.
3 *
4 * Author  Mohamed Lamine Karaoui (2015)
5 *         Alain Greiner (2016)
6 *
7 * Copyright (c) UPMC Sorbonne Universites
8 *
9 * This file is part of ALMOS-MKH.
10 *
11 * ALMOS-MKH is free software; you can redistribute it and/or modify it
12 * under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; version 2.0 of the License.
14 *
15 * ALMOS-MKH is distributed in the hope that it will be useful, but
16 * WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18 * General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with ALMOS-MKH; if not, write to the Free Software Foundation,
22 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23 */
24
25
[14]26#include <kernel_config.h>
[1]27#include <hal_types.h>
28#include <hal_atomic.h>
29#include <hal_special.h>
30#include <readlock.h>
31#include <spinlock.h>
32#include <printk.h>
33#include <list.h>
34#include <xlist.h>
35#include <slist.h>
36#include <xhtab.h>
[23]37#include <rpc.h>
[1]38#include <errno.h>
39#include <kmem.h>
40#include <mapper.h>
41#include <thread.h>
42#include <process.h>
[23]43#include <vfs.h>
[1]44#include <fatfs.h>
45#include <ramfs.h>
[23]46#include <devfs.h>
47#include <syscalls.h>
[1]48
49
50//////////////////////////////////////////////////////////////////////////////////////////
51//           Global variables         
52//////////////////////////////////////////////////////////////////////////////////////////
53
[23]54// array of supported FS contexts
[1]55vfs_ctx_t   fs_context[FS_TYPES_NR];
56
57//////////////////////////////////////////////////////////////////////////////////////////
58//           Context related functions
59//////////////////////////////////////////////////////////////////////////////////////////
60
[23]61////////////////////////////////////////////
[1]62error_t vfs_ctx_inum_alloc( vfs_ctx_t * ctx,
63                            uint32_t  * inum )
64{
65    // get lock on inum allocator
66    spinlock_lock( &ctx->lock );
67
68    // get lid from local inum allocator
[23]69    uint32_t lid = bitmap_ffc( ctx->bitmap , CONFIG_VFS_MAX_INODES );
[1]70
71    if( lid == -1 )   // no more free slot => error
72    {
73        // release lock
74        spinlock_unlock( &ctx->lock );
75
76        // return error
77        return 1;
78    }
79    else              // found => return inum
80    {
81        // set slot allocated
[23]82        bitmap_set( ctx->bitmap , lid );
[1]83
84        // release lock
85        spinlock_unlock( &ctx->lock );
86
87        // return inum
88        *inum = (((uint32_t)local_cxy) << 16) | (lid & 0xFFFF);
89        return 0;
90    }
91}
92
93////////////////////////////////////////////
94void vfs_ctx_inum_release( vfs_ctx_t * ctx,
95                           uint32_t    inum )
96{
[23]97    bitmap_clear( ctx->bitmap , inum & 0xFFFF ); 
[1]98}
99
100//////////////////////////////////////////////////////////////////////////////////////////
101//           Inode related functions
102//////////////////////////////////////////////////////////////////////////////////////////
103
[23]104//////////////////////////////////////////////////////
105
106error_t vfs_inode_create( xptr_t            dentry_xp,
107                          vfs_fs_type_t     fs_type,
108                          vfs_inode_type_t  inode_type,
109                          uint32_t          attr,
110                          uint32_t          rights,
111                          uid_t             uid,
112                          gid_t             gid,
113                          xptr_t          * inode_xp )
[1]114{
115    mapper_t         * mapper;     // associated mapper( to be allocated)
116    vfs_inode_t      * inode;      // inode descriptor (to be allocated)
117    uint32_t           inum;       // inode identifier (to be allocated)
118    vfs_ctx_t        * ctx;        // file system context
119        kmem_req_t         req;        // request to kernel memory allocator
120    error_t            error;
121
[23]122    // check fs type and get pointer on context
123    if     ( fs_type == FS_TYPE_FATFS ) ctx = &fs_context[FS_TYPE_FATFS];
124    else if( fs_type == FS_TYPE_RAMFS ) ctx = &fs_context[FS_TYPE_RAMFS];
125    else if( fs_type == FS_TYPE_DEVFS ) ctx = &fs_context[FS_TYPE_DEVFS];
[1]126    else
127    {
128        ctx = NULL;
129        printk("\n[PANIC] in %s : undefined file system type\n", __FUNCTION__ );
130        hal_core_sleep();
131    }
132
133    // allocate inum
134    error = vfs_ctx_inum_alloc( ctx , &inum );
135
136    if( error )
137    {
138        printk("\n[ERROR] in %s : cannot allocate inum\n", __FUNCTION__ );
139        return ENOMEM;
140    }
141
142    // allocate memory for mapper
143    mapper = mapper_create();
144
145    if( mapper == NULL )
146    {
147        printk("\n[ERROR] in %s : cannot allocate mapper\n", __FUNCTION__ );
148        vfs_ctx_inum_release( ctx , inum );
149        return ENOMEM;
150    }
151
[23]152    // allocate memory for VFS inode descriptor
[1]153        req.type  = KMEM_VFS_INODE;
154        req.size  = sizeof(vfs_inode_t);
155    req.flags = AF_KERNEL | AF_ZERO;
156        inode     = (vfs_inode_t *)kmem_alloc( &req );
157
158    if( inode == NULL )
159    {
160        printk("\n[ERROR] in %s : cannot allocate inode descriptor\n", __FUNCTION__ );
161        vfs_ctx_inum_release( ctx , inum );
162        mapper_destroy( mapper );
163        return ENOMEM;
164    }
165
166    // initialize inode descriptor
167    inode->gc         = 0;
[23]168    inode->type       = inode_type;
[1]169    inode->inum       = inum;
170    inode->attr       = attr;
[23]171    inode->rights     = rights;
[1]172    inode->uid        = uid;
173    inode->gid        = gid;
174    inode->refcount   = 0;
175    inode->parent_xp  = dentry_xp;
176    inode->ctx        = ctx;
177    inode->mapper     = NULL; 
178
179    // initialise threads waiting queue
180    xlist_root_init( XPTR( local_cxy , &inode->wait_root ) );
181
182    // initialize dentries hash table, if new inode is a directory
[23]183    if( inode_type == INODE_TYPE_DIR ) xhtab_init( &inode->children , XHTAB_DENTRY_TYPE );
[1]184
185    // initialize inode locks
[10]186    remote_rwlock_init( XPTR( local_cxy , &inode->data_lock ) );
[1]187    remote_spinlock_init( XPTR( local_cxy , &inode->main_lock ) );
188
189    // return extended pointer on inode
190    *inode_xp = XPTR( local_cxy , inode );
191    return 0;
192
193}  // end vfs_inode_create() 
194
195/////////////////////////////////////////////
196void vfs_inode_destroy( vfs_inode_t * inode )
197{
198    if( inode->refcount )
199    {
200        printk("\n[PANIC] in %s : inode refcount non zero\n", __FUNCTION__ );
201        hal_core_sleep(); 
202    }       
203
204    // release memory allocated for mapper
205    mapper_destroy( inode->mapper );
206
207    // release memory allocate for inode descriptor
208        kmem_req_t req;
209        req.ptr   = inode;
210        req.type  = KMEM_VFS_INODE;
211        kmem_free( &req );
212
213}  // end vfs_inode_destroy()
214
215////////////////////////////////////////////
216void vfs_inode_remote_up( xptr_t  inode_xp )
217{
218    // get inode cluster and local pointer
219    cxy_t         inode_cxy = GET_CXY( inode_xp );
220    vfs_inode_t * inode_ptr = (vfs_inode_t *)GET_PTR( inode_xp );
221
222    hal_remote_atomic_add( XPTR( inode_cxy , &inode_ptr->refcount ) , 1 );   
223}
224
225//////////////////////////////////////////////
226void vfs_inode_remote_down( xptr_t  inode_xp )
227{
228    // get inode cluster and local pointer
229    cxy_t         inode_cxy = GET_CXY( inode_xp );
230    vfs_inode_t * inode_ptr = (vfs_inode_t *)GET_PTR( inode_xp );
231
232    hal_remote_atomic_add( XPTR( inode_cxy , &inode_ptr->refcount ) , -1 );   
233}
234
235//////////////////////////////////////////////
236uint32_t vfs_inode_get_size( xptr_t inode_xp )
237{
238    // get inode cluster and local pointer
239    cxy_t         cxy = GET_CXY( inode_xp );
240    vfs_inode_t * ptr = (vfs_inode_t *)GET_PTR( inode_xp );
241
242    // get size
[10]243    remote_rwlock_rd_lock( XPTR( cxy , &ptr->data_lock ) );
[1]244    uint32_t size = hal_remote_lw( XPTR( cxy , &ptr->size ) );
[10]245    remote_rwlock_rd_unlock( XPTR( cxy , &ptr->data_lock ) );
[1]246    return size;
247}
248
249/////////////////////////////////////////////////
250void vfs_inode_size_set_size( xptr_t    inode_xp,
251                              uint32_t  size )
252{
253    // get inode cluster and local pointer
254    cxy_t         cxy = GET_CXY( inode_xp );
255    vfs_inode_t * ptr = (vfs_inode_t *)GET_PTR( inode_xp );
256
257    // set size
[10]258    remote_rwlock_wr_unlock( XPTR( cxy , &ptr->data_lock ) );
[1]259    hal_remote_sw( XPTR( cxy , &ptr->size ) , size );
[10]260    remote_rwlock_wr_unlock( XPTR( cxy , &ptr->data_lock ) );
[1]261}
262
263///////////////////////////////////////////////
264void vfs_inode_remote_unlock( xptr_t inode_xp )
265{
266    // get inode cluster and local pointer
267    cxy_t         cxy = GET_CXY( inode_xp );
268    vfs_inode_t * ptr = (vfs_inode_t *)GET_PTR( inode_xp );
269
270    // release the main lock
271    remote_spinlock_unlock( XPTR( cxy , &ptr->main_lock ) );
272}
273
274/////////////////////////////////////////////
275void vfs_inode_remote_lock( xptr_t inode_xp )
276{
277    // get inode cluster and local pointer
278    cxy_t         cxy = GET_CXY( inode_xp );
279    vfs_inode_t * ptr = (vfs_inode_t *)GET_PTR( inode_xp );
280
281    // get the main lock
282    remote_spinlock_lock( XPTR( cxy , &ptr->main_lock ) );
283}
284
285//////////////////////////////////////////////////////////////////////////////////////////
286//           Dentry related functions
287//////////////////////////////////////////////////////////////////////////////////////////
288
[23]289///////////////////////////////////////////////////
290error_t vfs_dentry_create( vfs_fs_type_t   fs_type,
291                           char          * name,
292                           vfs_inode_t   * parent,
293                           xptr_t        * dentry_xp )
[1]294{
295    vfs_ctx_t      * ctx;        // context descriptor
296    vfs_dentry_t   * dentry;     // dentry descriptor (to be allocated)
297        kmem_req_t       req;        // request to kernel memory allocator
298
[23]299printk("\n            @@@ dentry_create : 0 / name = %s\n", name );
300
[1]301    // check type and get pointer on context
[23]302    if     ( fs_type == FS_TYPE_FATFS ) ctx = &fs_context[FS_TYPE_FATFS];
303    else if( fs_type == FS_TYPE_RAMFS ) ctx = &fs_context[FS_TYPE_RAMFS];
304    else if( fs_type == FS_TYPE_DEVFS ) ctx = &fs_context[FS_TYPE_DEVFS];
[1]305    else
306    {
307        ctx = NULL;
308        printk("\n[PANIC] in %s : undefined file system type\n", __FUNCTION__ );
309        hal_core_sleep();
310    }
311
312    // get name length
313    uint32_t length = strlen( name );
314
[23]315    if( length >= CONFIG_VFS_MAX_NAME_LENGTH )
[1]316    {
317        printk("\n[ERROR] in %s : name too long\n", __FUNCTION__ );
318        return EINVAL;
319    }
320
[23]321printk("\n            @@@ dentry_create : 1 / name = %s\n", name );
322
[1]323    // allocate memory for dentry descriptor
324        req.type  = KMEM_VFS_DENTRY;
325        req.size  = sizeof(vfs_dentry_t);
326    req.flags = AF_KERNEL | AF_ZERO;
327        dentry     = (vfs_dentry_t *)kmem_alloc( &req );
328
329    if( dentry == NULL )
330    {
331        printk("\n[ERROR] in %s : cannot allocate dentry descriptor\n", __FUNCTION__ );
332        return ENOMEM;
333    }
334
335    // initialize dentry descriptor
[23]336
[1]337    dentry->ctx     = ctx;
338    dentry->length  = length;
339    dentry->parent  = parent;
340    strcpy( dentry->name , name );
341
[23]342printk("\n            @@@ dentry_create : 2 / name = %s\n", name );
343
344    // register dentry in hash table rooted in parent inode
345    xhtab_insert( XPTR( local_cxy , &parent->children ),
346                  name, 
347                  XPTR( local_cxy , &dentry->xlist ) );
348
349printk("\n            @@@ dentry_create : 3 / name = %s\n", name );
350
351    // return extended pointer on dentry
[1]352    *dentry_xp = XPTR( local_cxy , dentry );
353
354    return 0;
355
356}  // end vfs_dentry_create()
357
358////////////////////////////////////////////////
359void vfs_dentry_destroy( vfs_dentry_t * dentry )
360{
361    if( dentry->refcount )
362    {
363        printk("\n[PANIC] in %s : dentry refcount non zero\n", __FUNCTION__ );
364        hal_core_sleep(); 
365    }       
366
367        kmem_req_t req;
368        req.ptr   = dentry;
369        req.type  = KMEM_VFS_DENTRY;
370        kmem_free( &req );
371}
372
373
374//////////////////////////////////////////////////////////////////////////////////////////
375//           File descriptor related functions
376//////////////////////////////////////////////////////////////////////////////////////////
377
[23]378/////////////////////////////////////////////
379error_t vfs_file_create( vfs_inode_t * inode,
380                         uint32_t      attr,
381                         xptr_t      * file_xp )
382{
383    vfs_file_t  * file;
384        kmem_req_t    req;
385
386    // allocate memory for new file descriptor
387        req.type  = KMEM_VFS_FILE;
388        req.size  = sizeof(vfs_file_t);
389    req.flags = AF_KERNEL | AF_ZERO;
390        file      = (vfs_file_t *)kmem_alloc( &req );
391
392    if( file == NULL ) return ENOMEM;
393
394    // initializes new file descriptor
395    file->gc       = 0;
396    file->type     = inode->type;
397    file->attr     = attr;
398    file->offset   = 0;
399    file->refcount = 0;
400    file->inode    = inode;
401    file->ctx      = inode->ctx;
402    file->mapper   = inode->mapper;
403
404    remote_rwlock_init( XPTR( local_cxy , &file->lock ) );
405
406    *file_xp = XPTR( local_cxy , file );
407    return 0;
408
409}  // end vfs_file_create()
410
411///////////////////////////////////////////
412void vfs_file_destroy( vfs_file_t *  file )
413{
414    if( file->refcount )
415    {
416        printk("\n[PANIC] in %s : file refcount non zero\n", __FUNCTION__ );
417        hal_core_sleep(); 
418    }       
419
420        kmem_req_t req;
421        req.ptr   = file;
422        req.type  = KMEM_VFS_FILE;
423        kmem_free( &req );
424
425}  // end vfs_file_destroy()
426
427
[1]428////////////////////////////////////////
429void vfs_file_count_up( xptr_t file_xp )
430{
431    // get file cluster and local pointer
432    cxy_t        file_cxy = GET_CXY( file_xp );
433    vfs_file_t * file_ptr = (vfs_file_t *)GET_PTR( file_xp ); 
434
435    // atomically increment count
436    hal_remote_atomic_add( XPTR( file_cxy , &file_ptr->refcount ) , 1 ); 
437}
438
439//////////////////////////////////////////
440void vfs_file_count_down( xptr_t file_xp )
441{
442    // get file cluster and local pointer
443    cxy_t        file_cxy = GET_CXY( file_xp );
444    vfs_file_t * file_ptr = (vfs_file_t *)GET_PTR( file_xp ); 
445
446    // atomically decrement count
447    hal_remote_atomic_add( XPTR( file_cxy , &file_ptr->refcount ) , -1 ); 
448}
449
[23]450//////////////////////////////////////////////////////////////////////////////////////////
451//           File access related functions
452//////////////////////////////////////////////////////////////////////////////////////////
453
454////////////////////////////////////
455error_t vfs_open( xptr_t     cwd_xp,
456                          char     * path,
457                          uint32_t   flags,
458                  uint32_t   mode, 
459                          xptr_t   * new_file_xp,
460                  uint32_t * new_file_id )
[1]461{
[23]462    error_t       error;
463    xptr_t        inode_xp;     // extended pointer on target inode
464    cxy_t         inode_cxy;    // inode cluster identifier       
465    vfs_inode_t * inode_ptr;    // inode local pointer
466    uint32_t      file_attr;    // file descriptor attributes
467    uint32_t      lookup_mode;  // lookup working mode       
468    xptr_t        file_xp;      // extended pointer on created file descriptor
469    uint32_t      file_id;      // created file descriptor index in reference fd_array
[1]470
[23]471    // compute lookup working mode
472    lookup_mode = VFS_LOOKUP_OPEN;
473    if( (flags & O_DIR    )      )  lookup_mode |= VFS_LOOKUP_DIR;
474    if( (flags & O_CREAT  )      )  lookup_mode |= VFS_LOOKUP_CREATE;
475    if( (flags & O_EXCL   )      )  lookup_mode |= VFS_LOOKUP_EXCL;
476 
477    // compute attributes for the created file
478    file_attr = 0;
479    if( (flags & O_RDONLY ) == 0 )  file_attr |= FD_ATTR_READ_ENABLE;
480    if( (flags & O_WRONLY ) == 0 )  file_attr |= FD_ATTR_WRITE_ENABLE;
481    if( (flags & O_SYNC   )      )  file_attr |= FD_ATTR_SYNC;
482    if( (flags & O_APPEND )      )  file_attr |= FD_ATTR_APPEND;
483    if( (flags & O_CLOEXEC)      )  file_attr |= FD_ATTR_CLOSE_EXEC;
[1]484
[23]485    // get extended pointer on target inode
486    error = vfs_lookup( cwd_xp , path , lookup_mode , &inode_xp );
487
488    if( error ) return error;
489
490    // get target inode cluster and local pointer
491    inode_cxy = GET_CXY( inode_xp );
492    inode_ptr = (vfs_inode_t *)GET_PTR( inode_xp );
493   
494    // create a new file descriptor in cluster containing inode
495    if( inode_cxy == local_cxy )      // target cluster is local
[1]496    {
[23]497        error = vfs_file_create( inode_ptr , file_attr , &file_xp );
[1]498    }
[23]499    else                              // target cluster is remote
500    {
501        rpc_vfs_file_create_client( inode_cxy , inode_ptr , file_attr , &file_xp , &error );
502    }
[1]503
[23]504    if( error )  return error;
[1]505
[23]506    // allocate and register a new file descriptor index in reference cluster fd_array
507    error = process_fd_register( file_xp , &file_id );
[1]508
[23]509    if( error ) return error;
[1]510
[23]511    // success
512    *new_file_xp = file_xp;
513    *new_file_id = file_id;
514    return 0;
[1]515
[23]516}  // end vfs_open()
517
518/////////////////////////////////////
519error_t vfs_move( bool_t   to_buffer,
520                  xptr_t   file_xp,
521                  void   * buffer,
522                  uint32_t size )
523{
524    assert( ( file_xp != XPTR_NULL ) , __FUNCTION__ , "file_xp == XPTR_NULL" );
525
526    cxy_t              file_cxy;     // remote file descriptor cluster
527    vfs_file_t       * file_ptr;     // remote file descriptor local pointer
528    vfs_inode_type_t   inode_type;
529    uint32_t           file_offset;  // current offset in file
530    mapper_t         * mapper;
531    error_t            error;
532
533    // get cluster and local pointer on remote file descriptor
534    file_cxy  = GET_CXY( file_xp );
535    file_ptr  = (vfs_file_t *)GET_PTR( file_xp );
536
537    // get inode type from remote file descriptor
538    inode_type = hal_remote_lw( XPTR( file_cxy , &file_ptr->type   ) );
539   
540    // action depends on inode type
541    if( inode_type == INODE_TYPE_FILE )
542    {
543        // get mapper pointer and file offset from file descriptor
544        file_offset = hal_remote_lw( XPTR( file_cxy , &file_ptr->offset ) );
545        mapper = (mapper_t *)hal_remote_lpt( XPTR( file_cxy , &file_ptr->mapper ) );
546
547        // move data between mapper and buffer
548        if( file_cxy == local_cxy )
549        {
550            error = mapper_move( mapper,
551                                 to_buffer,
552                                 file_offset,
553                                 buffer,
554                                 size );
555        }
556        else
557        {
558            rpc_mapper_move_client( file_cxy,
559                                    mapper,
560                                    to_buffer,
561                                    file_offset,
562                                    buffer,
563                                    size,
564                                    &error );
565        } 
566
567        return error;
568    }
569    else if (inode_type == INODE_TYPE_DIR )
570    {
571        printk("\n[ERROR] in %s : inode is a directory", __FUNCTION__ );
572        return EINVAL;
573    }
574    else if (inode_type == INODE_TYPE_DEV )
575    {
576        // TODO
577        return 0;
578    }
579    else
580    {
581        printk("\n[PANIC] in %s : illegal inode type\n", __FUNCTION__ );
582        hal_core_sleep();
583        return -1;
584    }
585}  // end vfs_access()
586
587//////////////////////////////////////
588error_t vfs_lseek( xptr_t     file_xp,
589                   uint32_t   offset,
590                   uint32_t   whence, 
591                   uint32_t * new_offset )
592{
593    printk("\n[PANIC] %s non implemented\n", __FUNCTION__ );
594    hal_core_sleep();
[1]595    return 0;
596
[23]597    assert( ( file_xp != XPTR_NULL ) , __FUNCTION__ , "file_xp == XPTR_NULL" );
598
599}  // vfs_lseek()
600
601///////////////////////////////////
602error_t vfs_close( xptr_t   file_xp,
603                   uint32_t file_id )
[1]604{
[23]605    assert( (file_xp != XPTR_NULL) , __FUNCTION__ , "file_xp == XPTR_NULL" );
606
607    assert( (file_id < CONFIG_PROCESS_FILE_MAX_NR) , __FUNCTION__ , "illegal file_id" );
608
609    thread_t  * this    = CURRENT_THREAD;
610    process_t * process = this->process;
611
612    // get cluster and local pointer on remote file descriptor
[1]613    cxy_t        file_cxy = GET_CXY( file_xp );
614    vfs_file_t * file_ptr = (vfs_file_t *)GET_PTR( file_xp );
615
[23]616    // get local pointer on local cluster manager
617    cluster_t * cluster = LOCAL_CLUSTER;
618
619    // get owner process cluster and lpid
620    cxy_t   owner_cxy  = CXY_FROM_PID( process->pid );
621    lpid_t  lpid       = LPID_FROM_PID( process->pid );
622
623    // get extended pointers on copies root and lock
624    xptr_t root_xp = XPTR( owner_cxy , &cluster->pmgr.copies_root[lpid] );
625    xptr_t lock_xp = XPTR( owner_cxy , &cluster->pmgr.copies_lock[lpid] );
626
627    // take the lock protecting the copies
628    remote_spinlock_lock( lock_xp );
629
630    // 1) loop on the process descriptor copies to cancel all fd_array[file_id] entries
631    xptr_t  iter_xp;
632    XLIST_FOREACH( root_xp , iter_xp )
[1]633    {
[23]634        xptr_t      process_xp  = XLIST_ELEMENT( iter_xp , process_t , copies_list );
635        cxy_t       process_cxy = GET_CXY( process_xp );
636        process_t * process_ptr = (process_t *)GET_PTR( process_xp );
[1]637
[23]638        xptr_t lock_xp  = XPTR( process_cxy , &process_ptr->fd_array.lock );
639        xptr_t entry_xp = XPTR( process_cxy , &process_ptr->fd_array.array[file_id] );
640
641        // lock is required for atomic write of a 64 bits word
642        remote_rwlock_wr_lock( lock_xp );
643        hal_remote_swd( entry_xp , XPTR_NULL );
644        remote_rwlock_wr_unlock( lock_xp );
645
646        hal_wbflush();
647    }   
648
649    // 2) release memory allocated to file descriptor in remote cluster
650    if( file_cxy == local_cxy )             // file cluster is local
[1]651    {
[23]652        vfs_file_destroy( file_ptr );
653    }
654    else                                    // file cluster is local
655    {
656        rpc_vfs_file_destroy_client( file_cxy , file_ptr );
657    }
[1]658
[23]659    return 0;
[1]660
[23]661}  // end vfs_close()
[1]662
663////////////////////////////////////
[23]664error_t vfs_unlink( xptr_t   cwd_xp,
665                    char   * path )
[1]666{
[23]667    printk("\n[PANIC] %s non implemented\n", __FUNCTION__ );
668    hal_core_sleep();
[1]669    return 0;
[23]670}  // vfs_unlink()
[1]671
[23]672///////////////////////////////////////
673error_t vfs_stat( xptr_t       file_xp,
674                  vfs_stat_t * k_stat )
[1]675{
[23]676    printk("\n[PANIC] %s non implemented\n", __FUNCTION__ );
677    hal_core_sleep();
[1]678    return 0;
679}
680
[23]681////////////////////////////////////////////
682error_t vfs_readdir( xptr_t         file_xp,
683                     vfs_dirent_t * k_dirent )
[1]684{
[23]685    printk("\n[PANIC] %s non implemented\n", __FUNCTION__ );
686    hal_core_sleep();
[1]687    return 0;
688}
689
690//////////////////////////////////////
[23]691error_t vfs_mkdir( xptr_t     file_xp,
692                   char     * path,
693                   uint32_t   mode )
[1]694{
[23]695    printk("\n[PANIC] %s non implemented\n", __FUNCTION__ );
696    hal_core_sleep();
[1]697    return 0;
698}
699
[23]700////////////////////////////////////
701error_t vfs_rmdir( xptr_t   file_xp,
702                   char   * path )
[1]703{
[23]704    printk("\n[PANIC] %s non implemented\n", __FUNCTION__ );
705    hal_core_sleep();
[1]706    return 0;
707}
708
[23]709///////////////////////////////////
710error_t vfs_chdir( xptr_t   cwd_xp,
711                   char   * path )
[1]712{
[23]713    error_t           error;
714    xptr_t            inode_xp;     // extended pointer on target inode
715    cxy_t             inode_cxy;    // target inode cluster identifier       
716    vfs_inode_t     * inode_ptr;    // target inode local pointer
717    uint32_t          mode;         // lookup working mode       
718    vfs_inode_type_t  inode_type;   // target inode type
719
720    // set lookup working mode
721    mode = 0;
722
723    // get extended pointer on target inode
724    error = vfs_lookup( cwd_xp , path , mode , &inode_xp );
725
726    if( error ) return error;
727
728    // get inode cluster and local pointer
729    inode_cxy = GET_CXY( inode_xp );
730    inode_ptr = (vfs_inode_t *)GET_PTR( inode_xp );
731
732    // get inode type from remote file
733    inode_type = hal_remote_lw( XPTR( inode_cxy , &inode_ptr->type ) );
734
735    if( inode_type != INODE_TYPE_DIR )
736    {
737        CURRENT_THREAD->errno = ENOTDIR;
738        return -1;
739    }
740
741    printk("\n[PANIC] %s non fully implemented\n", __FUNCTION__ );
742    hal_core_sleep();
[1]743    return 0;
744}
745
[23]746///////////////////////////////////
747error_t vfs_chmod( xptr_t   cwd_xp,
748                   char   * path,
749                   uint32_t rights )
[1]750{
[23]751    error_t           error;
752    xptr_t            inode_xp;     // extended pointer on target inode
753    cxy_t             inode_cxy;    // inode cluster identifier       
754    vfs_inode_t     * inode_ptr;    // inode local pointer
755    uint32_t          mode;         // lookup working mode
756    vfs_inode_type_t  inode_type;   // target inode type
757
758    // set lookup working mode
759    mode = 0;
760 
761    // get extended pointer on target inode
762    error = vfs_lookup( cwd_xp , path , mode , &inode_xp );
763
764    if( error ) return error;
765
766    // get inode cluster and local pointer
767    inode_cxy = GET_CXY( inode_xp );
768    inode_ptr = (vfs_inode_t *)GET_PTR( inode_xp );
769   
770    // get inode type from remote inode
771    inode_type = hal_remote_lw( XPTR( inode_cxy , &inode_ptr->type ) );
772
773   
774    printk("\n[PANIC] %s non fully implemented\n", __FUNCTION__ );
775    hal_core_sleep();
[1]776    return 0;
777}
778
[23]779///////////////////////////////////
780error_t vfs_mkfifo( xptr_t   cwd_xp,
781                    char   * path,
782                    uint32_t rights )
783{
784    printk("\n[PANIC] in %s : not implemented yet\n", __FUNCTION__ );
785    hal_core_sleep(); 
786    return 0;
787}
[1]788
789
790
[23]791/////////////////////////////////////////////////////////////////////////////////////////r
[1]792//            Inode Tree functions
793//////////////////////////////////////////////////////////////////////////////////////////
794
795//////////////////////////////////////////////////////////////////////////////////////////
[23]796// This function is used by the vfs_lookup() function.
[1]797// It takes an extended pointer on a remote inode (parent directory inode),
798// and check access_rights violation for the calling thread.
799// It can be used by any thread running in any cluster.
800//////////////////////////////////////////////////////////////////////////////////////////
801// @ inode_xp    : extended pointer on inode.
802// @ client_uid  : client thread user ID
803// @ client_gid  : client thread group ID
804// @ return true if access rights are violated.
805//////////////////////////////////////////////////////////////////////////////////////////
806bool_t vfs_access_denied( xptr_t   inode_xp,
807                          uint32_t client_uid,
808                          uint32_t client_gid )
809{
810    // get found inode cluster and local pointer
811    cxy_t         inode_cxy = GET_CXY( inode_xp );
812    vfs_inode_t * inode_ptr = (vfs_inode_t *)GET_PTR( inode_xp );
813
814    // get inode access mode, UID, and GID
815    // TODO uint32_t  mode = hal_remote_lw( XPTR( inode_cxy , &inode_ptr->mode ) );
816    uid_t     uid  = hal_remote_lw( XPTR( inode_cxy , &inode_ptr->uid  ) );
817    gid_t     gid  = hal_remote_lw( XPTR( inode_cxy , &inode_ptr->gid  ) );
818
819    // FIXME : me must use mode
820    if( (uid == client_uid) || (gid == client_gid) ) return false;
821    else                                             return true;
822}
823
824//////////////////////////////////////////////////////////////////////////////////////////
825// This static function is used by the vfs_lookup() function.
826// It takes an extended pointer on a remote inode (parent directory inode), a directory
827// entry name, and returns an extended pointer on the child inode.
828// It can be used by any thread running in any cluster.
829//////////////////////////////////////////////////////////////////////////////////////////
830// @ parent_xp   : extended pointer on parent inode in remote cluster.
831// @ name        : dentry name
832// @ child_xp    : [out] buffer for extended pointer on child inode.
833// @ return true if success / return false if not found.
834//////////////////////////////////////////////////////////////////////////////////////////
835static bool_t vfs_get_child( xptr_t   parent_xp,
836                             char   * name,
837                             xptr_t * child_xp )
838{
839    xptr_t  xhtab_xp;    // extended pointer on hash table containing children dentries
840    xptr_t  dentry_xp;   // extended pointer on children dentry
841
842    // get parent inode cluster and local pointer
843    cxy_t         parent_cxy = GET_CXY( parent_xp );
844    vfs_inode_t * parent_ptr = (vfs_inode_t *)GET_PTR( parent_xp );
845
846    // get extended pointer on hash table of children directory entries
847    xhtab_xp = XPTR( parent_cxy , &parent_ptr->children );
848
849    // search extended pointer on matching dentry
850    dentry_xp = xhtab_lookup( xhtab_xp , name );
851
852    if( dentry_xp == XPTR_NULL ) return false;
853
854    // get dentry cluster and local pointer
855    cxy_t          dentry_cxy = GET_CXY( dentry_xp );
856    vfs_dentry_t * dentry_ptr = (vfs_dentry_t *)GET_PTR( dentry_xp );
857
858    // return child inode
859    *child_xp = (xptr_t)hal_remote_lwd( XPTR( dentry_cxy , &dentry_ptr->parent ) );
860    return true;
861}
862
863//////////////////////////////////////////////////////////////////////////////////////////
864// This static function is used by the vfs_lookup() function.
865// It takes the <current> pointer on a buffer containing a complete pathname, and return
866// in the <name> buffer, allocated by the caller, a single name in the path.
867// It return also in the <next> pointer the next character to analyse in the path.
868// Finally it returns a <last> boolean, that is true when the returned <name> is the
869// last name in the path. The names are supposed to be separated by one or several '/'
870// characters, that are not written in  the <name> buffer.
871//////////////////////////////////////////////////////////////////////////////////////////
872// @ current   : pointer on first character to analyse in buffer containing the path.
873// @ name      : [out] pointer on buffer allocated by the caller for the returned name.
874// @ next      : [out] pointer on next character to analyse in buffer containing the path.
875// @ last      : [out] true if the returned name is the last (NUL character found).
876// @ return 0 if success / return EINVAL if string empty (first chracter is NUL).
877//////////////////////////////////////////////////////////////////////////////////////////
878static error_t vfs_get_name_from_path( char     * current,
879                                       char     * name,
880                                       char    ** next,
881                                       bool_t   * last )
882{
883    char * ptr = current;
884
885    // skip leading '/' characters
886    while( *ptr == '/' ) ptr++;
887
888    // return EINVAL if string empty
889    if( *ptr == 0 ) return EINVAL;
890
891    // copy all characters in name until NUL or '/'
892    while( (*ptr != 0) && (*ptr !='/') )  *(name++) = *(ptr++);
893
894    // return last an next
895    if( *ptr == 0 )             // last found character is NUL => last name in path
896    {
897        *last = true;
898    }
899    else                        // last found character is '/' => skip it
900    {
901        *last = false;
902        *next = ptr + 1;
903    }
904
905    return 0;
906}
907
[23]908//////////////////////////////////////////////
909error_t vfs_lookup( xptr_t             cwd_xp,
910                    char             * pathname,
911                    uint32_t           mode,
912                                        xptr_t           * inode_xp )
[1]913{
914    char          name[CONFIG_VFS_MAX_NAME_LENGTH];   // one name in path
915
[23]916    xptr_t             parent_xp;    // extended pointer on parent inode
917    cxy_t              parent_cxy;   // cluster for parent inode
918    vfs_inode_t      * parent_ptr;   // local pointer on parent inode 
919    xptr_t             child_xp;     // extended pointer on child inode
920    cxy_t              child_cxy;    // cluster for child inode
921    vfs_inode_t      * child_ptr;    // local pointer on child inode 
922    vfs_inode_type_t   inode_type;   // child inode type
923    vfs_fs_type_t      fs_type;      // File system type
924    vfs_ctx_t        * ctx_ptr;      // local pointer on FS context
925    char             * current;      // current pointer on path
926    char             * next;         // next value for current pointer   
927    bool_t             last;         // true when the name is the last in path
928    bool_t             found;        // true when a child has been found
929    thread_t         * this;         // pointer on calling thread descriptor
930    process_t        * process;      // pointer on calling process descriptor
931    error_t            error;
[1]932
933    this    = CURRENT_THREAD;
934    process = this->process;
935
936    // get extended pointer on first inode to search
937    if( pathname[0] == '/' ) parent_xp = process->vfs_root_xp;
938    else                     parent_xp = cwd_xp;
939
940    // initialise loop variables
941    current  = pathname;
942    next     = NULL;
943    last     = false;
944    child_xp = XPTR_NULL;
945
946    // take lock on parent inode
947    vfs_inode_remote_lock( parent_xp );
948
949    // break : if one intermediate name not found
950    // exit  : when last name found (i.e. last == true)
951    do
952    {
[23]953        // get one name from path and the last flag
[1]954        vfs_get_name_from_path( current , name , &next , &last );
955
956        // search a child dentry matching name for parent inode
957        found = vfs_get_child( parent_xp,
958                               name,
959                               &child_xp );
960
961        if( found == false ) // child inode not found in inode tree => try to load it
962        {
963            // release lock on parent inode
964            vfs_inode_remote_unlock( parent_xp );
965
[23]966            // get cluster and local pointer on parent inode
967            parent_cxy = GET_CXY( parent_xp );
968            parent_ptr = (vfs_inode_t *)GET_PTR( parent_xp );
[1]969
[23]970            // get parent inode FS type
971            ctx_ptr = (vfs_ctx_t *)hal_remote_lpt( XPTR( parent_cxy , &parent_ptr->ctx ) );
972            fs_type = ctx_ptr->type;
973
974            // get child inode type
975            if( (last == false) || (mode & VFS_LOOKUP_DIR) ) inode_type = INODE_TYPE_DIR;
976            else                                             inode_type = INODE_TYPE_FILE;
977
978            // insert a child dentry/inode in parent inode
979            error = vfs_add_child_in_parent( inode_type,
980                                             fs_type, 
981                                             parent_xp, 
982                                             name, 
983                                             &child_xp );
984
[1]985            if( error )
986            {
987                printk("\n[ERROR] in %s : inode %s not found in path %s\n",
988                       __FUNCTION__ , name , pathname );
989                return ENOENT;
990            }
991
992            // take lock on parent inode
993            vfs_inode_remote_lock( parent_xp );
994        }
995
996        // check access rights
[23]997        // error = vfs_access_denied( child_xp,
998        //                            client_uid,
999        //                            client_gid );
1000        // if( error )
1001        // {
1002        //     printk("\n[ERROR] in %s : permission denied for %s\n", __FUNCTION__ , name );
1003        //     return EACCES;
1004        // }
[1]1005
1006        // take lock on child inode if not last
1007        if( last == false ) vfs_inode_remote_lock( child_xp );
1008
1009        // release lock on parent inode
1010        vfs_inode_remote_unlock( parent_xp );
1011
1012        // update loop variables
1013        parent_xp = child_xp;
1014        current   = next;
1015    }
1016    while( last == false );
1017
1018    vfs_dmsg("\n[INFO] in %s : searched inode found for %s\n",
1019                 __FUNCTION__ , pathname );
1020
1021    // get cluster and local pointer on child inode
1022    child_cxy = GET_CXY( child_xp );
1023    child_ptr = (vfs_inode_t *)GET_PTR( child_xp );
1024
1025    // return searched pointers
1026    *inode_xp = child_xp;
1027
1028    return 0;
1029
1030}  // end vfs_lookup()
1031
1032
1033////////////////////////////////////////////
1034error_t vfs_get_path( xptr_t    searched_xp,
1035                      char    * buffer,
1036                      uint32_t  max_size )
1037{
1038        xptr_t       dentry_xp;   // extended pointer on current dentry
1039    char       * name;        // local pointer on current dentry name
1040        uint32_t     length;      // length of current dentry name
1041        uint32_t     count;       // number of characters written in buffer
1042        uint32_t     index;       // slot index in buffer
[23]1043    xptr_t       inode_xp;    // extended pointer on   
[1]1044
1045    // implementation note:
1046    // we use two variables "index" and "count" because the buffer
1047    // is actually written in decreasing index order (from leaf to root)
1048    // TODO : handle conflict with a concurrent rename
1049    // FIXME : handle synchro in the loop ... [AG]
1050
1051        // set the NUL character in buffer / initialise buffer index and count
1052        buffer[max_size - 1] = 0;
1053        count    = 1;
1054    index    = max_size - 2;
1055
1056    // initialize current inode
1057    inode_xp  = searched_xp;
1058
1059    // exit when root inode found (i.e. dentry_xp == XPTR_NULL)
1060        do
1061    {
1062        // get inode cluster and local pointer
1063        cxy_t         inode_cxy = GET_CXY( inode_xp );
1064        vfs_inode_t * inode_ptr = (vfs_inode_t *)GET_PTR( inode_xp );
1065
1066        // get extended pointer on parent dentry               
1067        dentry_xp = (xptr_t)hal_remote_lwd( XPTR( inode_cxy , inode_ptr->parent_xp ) );
1068
1069        // get dentry cluster and local pointer
1070        cxy_t          dentry_cxy = GET_CXY( dentry_xp );
1071        vfs_dentry_t * dentry_ptr = (vfs_dentry_t *)GET_PTR( dentry_xp );
1072
1073        // get dentry name length and pointer
1074        length =  hal_remote_lw( XPTR( dentry_cxy , &dentry_ptr->length ) );
1075        name   = (char *)hal_remote_lpt( XPTR( dentry_cxy , &dentry_ptr->name ) );
1076
1077        // update index and count
1078        index -= (length + 1); 
1079        count += (length + 1);
1080
1081        // check buffer overflow
1082        if( count >= max_size )
1083        {
1084            printk("\n[ERROR] in %s : kernel buffer too small\n", __FUNCTION__ );
1085            return EINVAL;
1086        }
1087
1088        // update pathname
1089        hal_remote_memcpy( XPTR( local_cxy , &buffer[index + 1] ) ,
1090                           XPTR( dentry_cxy , name ) , length );
1091                buffer[index] = '/';
1092
1093                // get extended pointer on next inode
1094        inode_xp = (xptr_t)hal_remote_lwd( XPTR( dentry_cxy , dentry_ptr->parent ) );
1095    }
1096    while( (dentry_xp != XPTR_NULL) );
1097
1098        return 0;
1099
1100}  // end vfs_get_path()
1101
[23]1102///////////////////////////////////////////////////////////////
1103error_t vfs_add_child_in_parent( vfs_inode_type_t   inode_type,
1104                                 vfs_fs_type_t      fs_type,
1105                                 xptr_t             parent_xp,
1106                                 char             * name,
1107                                 xptr_t           * child_xp )
[1]1108{
[23]1109    error_t         error;
1110    xptr_t          dentry_xp;   // extended pointer on created dentry
1111    xptr_t          inode_xp;    // extended pointer on created inode
1112    cxy_t           parent_cxy;  // parent inode cluster identifier
1113    vfs_inode_t   * parent_ptr;  // parent inode local pointer
1114    vfs_ctx_t     * parent_ctx;  // parent inode context local pointer
[1]1115
1116    // get parent inode cluster and local pointer
[23]1117    parent_cxy = GET_CXY( parent_xp );
1118    parent_ptr = (vfs_inode_t *)GET_PTR( parent_xp );
[1]1119
[23]1120    // get parent inode context local pointer
1121    parent_ctx = (vfs_ctx_t *)hal_remote_lpt( XPTR( parent_cxy , &parent_ptr->ctx ) );
1122
[1]1123    // create dentry
1124    if( parent_cxy == local_cxy )      // parent cluster is the local cluster
1125    {
[23]1126        error = vfs_dentry_create( fs_type,
[1]1127                                   name,
1128                                   parent_ptr,
1129                                   &dentry_xp );
1130    }
1131    else                               // parent cluster is remote
1132    {
1133        rpc_vfs_dentry_create_client( parent_cxy,
[23]1134                                      fs_type,
[1]1135                                      name,
1136                                      parent_ptr,
1137                                      &dentry_xp,
1138                                      &error );
1139    }
1140                                     
1141    if( error )
1142    {
1143        printk("\n[ERROR] in %s : cannot create dentry in cluster %x\n",
1144               __FUNCTION__ , parent_cxy );
1145
1146        return error;
1147    }
1148
1149    // select a target cluster for child inode
1150    uint32_t  x_size    = LOCAL_CLUSTER->x_size;
1151    uint32_t  y_size    = LOCAL_CLUSTER->y_size;
1152    uint32_t  y_width   = LOCAL_CLUSTER->y_width;
1153    uint32_t  index     = ( hal_time_stamp() + hal_get_gid() ) % (x_size * y_size);
1154    uint32_t  x         = index / y_size;   
1155    uint32_t  y         = index % y_size;
1156    cxy_t     child_cxy = (x<<y_width) + y;
1157                                     
1158    // create child inode TODO : define attr / mode / uid / gid
1159    uint32_t attr = 0;
1160    uint32_t mode = 0;
1161    uint32_t uid  = 0;
1162    uint32_t gid  = 0;
1163   
1164    if( child_cxy == local_cxy )      // child cluster is the local cluster
1165    {
1166        error = vfs_inode_create( dentry_xp,
[23]1167                                  fs_type,
1168                                  inode_type,
[1]1169                                  attr,
1170                                  mode,
1171                                  uid,
1172                                  gid,
1173                                  &inode_xp );
1174    }
1175    else                              // child cluster is remote
1176    {
1177        rpc_vfs_inode_create_client( child_cxy,
1178                                     dentry_xp,
[23]1179                                     fs_type,
1180                                     inode_type,
[1]1181                                     attr,
1182                                     mode,
1183                                     uid,
1184                                     gid,
1185                                     &inode_xp,
1186                                     &error );
1187    }
1188                                     
1189    if( error )
1190    {
1191        printk("\n[ERROR] in %s : cannot create inode in cluster %x\n",
1192               __FUNCTION__ , child_cxy );
1193 
1194        vfs_dentry_t * dentry = (vfs_dentry_t *)GET_PTR( dentry_xp );
1195        if( parent_cxy == local_cxy ) vfs_dentry_destroy( dentry );
1196        else rpc_vfs_dentry_destroy_client( parent_cxy , dentry );
1197        return error;
1198    }
1199
1200    // success : return extended pointer on child inode
1201    *child_xp = inode_xp;
1202    return 0;
1203
1204}  // end vfs_add_child_in_parent()
1205
1206
[23]1207
1208
1209//////////////////////////////////////////////////////////////////////////////////////////
1210//            Mapper related functions
1211//////////////////////////////////////////////////////////////////////////////////////////
1212
1213////////////////////////////////////////////////
1214error_t vfs_move_page_to_mapper( page_t * page )
1215{
1216    error_t         error = 0;
1217
1218    assert( (page != NULL) , __FUNCTION__ , "page pointer is NULL\n" );
1219
1220    mapper_t * mapper = page->mapper;
1221
1222    assert( (mapper != NULL) , __FUNCTION__ , "no mapper for page\n" );
1223
1224    // get FS type
1225    vfs_fs_type_t fs_type = mapper->inode->ctx->type;
1226
1227    // update mapper if permitted by file system type
1228    if( fs_type == FS_TYPE_FATFS )
1229    {
1230        // get mapper lock in WRITE_MODE
1231        rwlock_wr_lock( &mapper->lock );
1232
1233        error = fatfs_read_page( page ); 
1234
1235        // release mapper lock
1236        rwlock_wr_unlock( &mapper->lock );
1237    }
1238    else if( fs_type == FS_TYPE_RAMFS )
1239    {
1240        assert( false , __FUNCTION__ , "should not be called for RAMFS\n" );
1241    }
1242    else if( fs_type == FS_TYPE_DEVFS )
1243    {
1244        assert( false , __FUNCTION__ , "should not be called for DEVFS\n" );
1245    }
1246    else
1247    {
1248        assert( false , __FUNCTION__ , "undefined file system type\n" );
1249    }
1250
1251    return error;
1252
1253}  // end vfs_move_page_to_mapper()
1254
1255//////////////////////////////////////////////////
1256error_t vfs_move_page_from_mapper( page_t * page )
1257{
1258    error_t         error = 0;
1259
1260    assert( (page != NULL) , __FUNCTION__ , "page pointer is NULL\n" );
1261
1262    mapper_t * mapper = page->mapper;
1263
1264    assert( (mapper != NULL) , __FUNCTION__ , "no mapper for page\n" );
1265
1266    // get FS type
1267    vfs_fs_type_t  fs_type = mapper->inode->ctx->type;
1268
1269    // update file system if permitted by file system type
1270    if( fs_type == FS_TYPE_FATFS )
1271    {
1272            if( page_is_flag( page , PG_DIRTY ) ) 
1273            {
1274            // get mapper lock in READ_MODE
1275            rwlock_rd_lock( &mapper->lock );
1276
1277            error = fatfs_write_page( page );
1278
1279            // release mapper lock from READ_MODE
1280            rwlock_rd_unlock( &mapper->lock );
1281
1282            // clear dirty bit if success
1283                    if( error == 0 ) page_undo_dirty( page );
1284         }
1285    }
1286    else if( fs_type == FS_TYPE_RAMFS )
1287    {
1288        assert( false , __FUNCTION__ , "should not be called for RAMFS\n" );
1289    }
1290    else if( fs_type == FS_TYPE_DEVFS )
1291    {
1292        assert( false , __FUNCTION__ , "should not be called for DEVFS\n" );
1293    }
1294    else
1295    {
1296        assert( false , __FUNCTION__ , "undefined file system type\n" );
1297    }
1298       
1299    return error;
1300
1301}  // end vfs_move_page_from_mapper()
1302
1303
Note: See TracBrowser for help on using the repository browser.