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

Last change on this file since 120 was 101, checked in by alain, 7 years ago

euh...

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