source: trunk/kernel/fs/fatfs/fat32_access.c @ 2

Last change on this file since 2 was 1, checked in by alain, 8 years ago

First import

File size: 16.4 KB
Line 
1/*
2 * fat32_access.c - FAT32 access functions implementation
3 *
4 * Author    Ghassan Almaless (2008,2009,2010,2011,2012)
5 *           Mohamed Lamine Karaoui (2015)
6 *           Alain Greiner (2016)
7 *
8 * Copyright (c) UPMC Sorbonne Universites
9 *
10 * This file is part of ALMOS-MKH.
11 *
12 * ALMOS-MKH.is free software; you can redistribute it and/or modify it
13 * under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; version 2.0 of the License.
15 *
16 * ALMOS-MKH.is distributed in the hope that it will be useful, but
17 * WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19 * General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with ALMOS-MKH.; if not, write to the Free Software Foundation,
23 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
24 */
25
26#include <driver.h>
27#include <device.h>
28#include <string.h>
29#include <rwlock.h>
30#include <page.h>
31#include <pmm.h>
32#include <thread.h>
33#include <cluster.h>
34#include <mapper.h>
35#include <vfs.h>
36
37#include <fat32.h>
38#include "fat32-private.h"
39
40#if VFAT_INSTRUMENT
41uint32_t blk_rd_count ;
42uint32_t rd_count     ;
43uint32_t wr_count     ;
44#endif
45
46RPC_DECLARE(__vfat_cluster_count, RPC_RET(RPC_RET_PTR(int, count)), 
47                        RPC_ARG(RPC_ARG_VAL(struct vfat_context_s*, ctx),
48                        RPC_ARG_VAL(vfat_cluster_t, cluster_index)))
49{
50        int cnt;
51        vfat_cluster_t next_cluster_index = 0;
52
53        cnt = 0;
54        while(next_cluster_index < 0x0FFFFFF8) {
55                vfat_query_fat(ctx, cluster_index, &next_cluster_index);
56                cluster_index = next_cluster_index;
57                cnt++;
58        }
59        *count = cnt;
60}
61
62int vfat_cluster_count(struct vfat_context_s *ctx, vfat_cluster_t cluster_index) 
63{
64        int count;
65        RCPC(ctx->fat_cid, RPC_PRIO_FS, __vfat_cluster_count, 
66                                        RPC_RECV(RPC_RECV_OBJ(count)), 
67                                        RPC_SEND(RPC_SEND_OBJ(ctx),
68                                        RPC_SEND_OBJ(cluster_index)));
69        return count;
70}
71
72RPC_DECLARE(__vfat_query_fat, RPC_RET(RPC_RET_PTR(error_t, err), 
73                        RPC_RET_PTR(vfat_cluster_t, next_cluster_index)),
74
75                        RPC_ARG(RPC_ARG_VAL(struct vfat_context_s*, ctx),
76                        RPC_ARG_VAL(vfat_cluster_t, cluster_index)))
77                       
78{
79        vfat_cluster_t *data;
80        vfat_cluster_t cluster_offset;
81        struct page_s *page;
82        struct mapper_s *mapper;
83        uint32_t page_id;
84
85        mapper = ctx->mapper;
86
87        cluster_offset = ctx->fat_begin_lba * ctx->bytes_per_sector + (cluster_index * 4);
88        page_id = cluster_offset >> PMM_PAGE_SHIFT;
89
90        vfat_dmsg(1, "%s: TID %x, Query FAT at page_id %d to get next cluster of %u\n",
91                  __FUNCTION__, CURRENT_THREAD, page_id, cluster_index);
92
93        if ((page = mapper_get_page(mapper, page_id, MAPPER_SYNC_OP)) == NULL)
94        {
95                *err = -1;     
96                return;
97        }
98
99        vfat_dmsg(1, "%s: cluster offset %d, offset %d\n", __FUNCTION__, cluster_offset, cluster_offset % PMM_PAGE_SIZE);
100
101        data = (vfat_sector_t*) ppm_page2addr(page);
102        data = (vfat_sector_t*)((uint8_t*) data + (cluster_offset % PMM_PAGE_SIZE));
103        *next_cluster_index = *data & 0x0FFFFFFF;
104
105        vfat_dmsg(1, "%s: next cluster for %u is %u\n", __FUNCTION__, cluster_index, *next_cluster_index);
106        *err = 0;
107}
108
109error_t vfat_query_fat(struct vfat_context_s* ctx,
110                vfat_cluster_t cluster_index,
111                vfat_cluster_t *next_cluster_index)
112{
113        error_t err;
114        vfat_cluster_t next;
115
116        RCPC(ctx->fat_cid, RPC_PRIO_FS, __vfat_query_fat, 
117                                RPC_RECV(RPC_RECV_OBJ(err), RPC_RECV_OBJ(next)),
118                                RPC_SEND(RPC_SEND_OBJ(ctx), RPC_SEND_OBJ(cluster_index)));
119
120        *next_cluster_index = next;
121        return err;
122}
123
124
125RPC_DECLARE(__vfat_alloc_fat_entry, RPC_RET(RPC_RET_PTR(error_t, err), RPC_RET_PTR(vfat_cluster_t,new_cluster)), 
126                                        RPC_ARG(RPC_ARG_VAL(struct vfat_context_s*, ctx)))
127{
128        struct page_s *page;
129        struct mapper_s *mapper;
130        vfat_sector_t EOF_sector;
131        vfat_sector_t last_allocated_sector;
132        vfat_sector_t sector;
133        uint32_t last_allocated_index;
134        uint32_t index;
135        uint32_t sectors_per_page;
136        uint32_t *buffer;
137        uint32_t found;
138        uint32_t sector_size;
139        uint32_t page_id;
140        uint32_t old_page_id;
141
142        sector_size = ctx->bytes_per_sector;
143        sectors_per_page = PMM_PAGE_SIZE / sector_size;
144        EOF_sector = ctx->fat_begin_lba + ctx->fat_blk_count;
145        *new_cluster = 0;
146        *err = 0;
147        found = 0;
148        old_page_id = 0xFFFFFFFF;
149        mapper = ctx->mapper;
150        page = NULL;
151
152        rwlock_wrlock(&ctx->lock);
153        last_allocated_sector = ctx->last_allocated_sector;
154        last_allocated_index = ctx->last_allocated_index;
155        vfat_dmsg(2, "%s: Started, last allocated sector %u\n", __FUNCTION__, last_allocated_sector);
156
157        for(sector = last_allocated_sector; (sector < EOF_sector) && (!found); sector ++)
158        {
159                vfat_dmsg(2, "%s: sector %u\n", __FUNCTION__, sector);
160                page_id = sector / sectors_per_page;
161
162                if (page_id != old_page_id) // Let's not reload the same page everytime
163                { 
164                        vfat_dmsg(2, "%s: current page #%d, next page #%d\n", __FUNCTION__, old_page_id, page_id);
165
166                        page = mapper_get_page(mapper, page_id, MAPPER_SYNC_OP);
167
168                        if(page == NULL)
169                        {
170                                printk(WARNING, "WARNING: alloc_fat: error reading sector %d\n", sector);
171                                *err = VFS_IO_ERR;
172                                goto VFAT_ALLOC_CLUSTER_ERROR;
173                        }
174                }
175
176                buffer = ppm_page2addr(page);
177                buffer = buffer + ((sector % sectors_per_page) * (sector_size / 4));
178
179                for(index = last_allocated_index; index < (sector_size/4); index ++)
180                {
181                        vfat_dmsg(2, "%s: index %d\n", __FUNCTION__, index);
182
183                        if((buffer[index] & 0x0FFFFFFF) == 0x00000000)
184                        {
185                                found = 1;
186
187                                page_lock(page);
188                                buffer[index] = 0x0FFFFFF8;
189                                mapper->m_ops->set_page_dirty(page);
190                                page_unlock(page);
191
192#if VFAT_INSTRUMENT
193                                wr_count ++;
194#endif
195                                vfat_dmsg(1,"%s: page #%d (@%x) is set to delayed write\n", __FUNCTION__, page->index, page);
196
197                                if(index != ((sector_size/4) - 1))
198                                {
199                                        ctx->last_allocated_sector = sector;
200                                        ctx->last_allocated_index = (index + 1) % (sector_size/4);
201                                } 
202                                else
203                                {
204                                        last_allocated_sector = sector + 1;
205
206                                        if(last_allocated_sector == EOF_sector) 
207                                        {
208                                                ctx->last_allocated_sector = ctx->fat_begin_lba;
209                                                ctx->last_allocated_index = 2;
210                                        } 
211                                        else 
212                                        {
213                                                ctx->last_allocated_sector = last_allocated_sector;
214                                                ctx->last_allocated_index = (index + 1) % (sector_size/4);
215                                        }
216                                }
217
218                                index += (sector - ctx->fat_begin_lba) * (sector_size/4);
219                                vfat_dmsg(2, "%s: found: cluster %u\n",  __FUNCTION__, index);
220                                *new_cluster = index;
221                                break;
222                        }
223                }
224
225                last_allocated_index = 0;
226                old_page_id = page_id;
227        }
228
229        //if(page)
230        // page_refcount_down(page);
231
232VFAT_ALLOC_CLUSTER_ERROR:
233        rwlock_unlock(&ctx->lock);
234        vfat_dmsg(2, "%s: new_cluster %u, hasError ? %d\n", __FUNCTION__, *new_cluster, *err);
235}
236
237
238error_t vfat_alloc_fat_entry(struct vfat_context_s* ctx, vfat_cluster_t *new_cluster)
239{
240        error_t err;
241        vfat_cluster_t new;
242
243        RCPC(ctx->fat_cid, RPC_PRIO_FS, __vfat_alloc_fat_entry, 
244                                RPC_RECV(RPC_RECV_OBJ(err), 
245                                        RPC_RECV_OBJ(new)),
246                                RPC_SEND(RPC_SEND_OBJ(ctx)));
247
248        *new_cluster=new;
249
250        return err;
251}
252
253
254RPC_DECLARE(__vfat_extend_cluster, RPC_RET(RPC_RET_PTR(error_t, err), 
255                                        RPC_RET_PTR(vfat_cluster_t, next_cluster)), 
256                                        RPC_ARG(RPC_ARG_VAL(struct vfat_context_s*, ctx), 
257                                        RPC_ARG_VAL(vfat_cluster_t, current_vfat_cluster)))
258{
259        struct page_s *page;
260        struct mapper_s *mapper;
261        vfat_sector_t lba;
262        vfat_cluster_t val;
263        size_t sector_size;
264        uint32_t sectors_per_page;
265        uint32_t *buffer;
266
267        sector_size = ctx->bytes_per_sector;
268        sectors_per_page = PMM_PAGE_SIZE / sector_size;
269        mapper = ctx->mapper;
270
271        if((*err = vfat_alloc_fat_entry(ctx, next_cluster)))
272                return;
273
274        if(*next_cluster == 0)
275        {
276                *err=0;
277        }
278
279        lba = ctx->fat_begin_lba + ((current_vfat_cluster *4) / sector_size);
280
281        page = mapper_get_page(mapper, lba / sectors_per_page, MAPPER_SYNC_OP);
282
283        if(page == NULL) goto VFAT_EXTEND_CLUSTER_ERROR;
284
285        vfat_dmsg(1, "%s: extending cluster %u\n", __FUNCTION__, current_vfat_cluster);
286
287        buffer = (uint32_t*) ppm_page2addr(page);
288        buffer += (lba % sectors_per_page) * (sector_size/4);
289
290        page_lock(page);
291
292        val = buffer[current_vfat_cluster % (sector_size/4)] & 0x0FFFFFFF;
293
294        if( val >= 0x0FFFFFF8)
295                buffer[current_vfat_cluster % (sector_size/4)] = *next_cluster;
296        else
297        {
298                vfat_dmsg(1,"%s: %u already extended !\n", __FUNCTION__, current_vfat_cluster);
299                vfat_dmsg(1,"%s: freeing %u\n", __FUNCTION__, *next_cluster);
300
301                page_unlock(page);
302                //page_refcount_down(page);
303
304                lba = ctx->fat_begin_lba + ((*next_cluster *4) / sector_size);
305
306                if((page = mapper_get_page(mapper, lba / sectors_per_page, MAPPER_SYNC_OP)) == NULL)
307                        goto VFAT_EXTEND_CLUSTER_ERROR;
308
309                buffer = (uint32_t*) ppm_page2addr(page);
310                buffer += (lba % sectors_per_page) * (sector_size/4);
311                page_lock(page);
312                buffer[*next_cluster % (sector_size/4)] = 0x00;
313        }
314
315#if VFAT_INSTRUMENT
316        wr_count ++;
317#endif
318
319        mapper->m_ops->set_page_dirty(page);
320
321        page_unlock(page);
322        vfat_dmsg(1,"%s: page #%d (@%x) is set to delayed write\n", __FUNCTION__, page->index, page);
323
324
325        vfat_dmsg(1,"%s: cluster %u's FAT entry is set to %u, FAT's sector %u is set as delayed write\n",
326                  __FUNCTION__, 
327                  current_vfat_cluster, 
328                  *next_cluster,
329                  lba);
330
331        vfat_dmsg(1,"%s: allocated cluster %u for asked cluster %u\n",
332                  __FUNCTION__, 
333                  *next_cluster, 
334                  current_vfat_cluster);
335        *err = 0;
336        return;
337
338VFAT_EXTEND_CLUSTER_ERROR:
339        vfat_free_fat_entry(ctx,*next_cluster);
340        *next_cluster = 0;
341        *err = -1;
342}
343
344error_t vfat_extend_cluster(struct vfat_context_s* ctx,
345                vfat_cluster_t current_vfat_cluster,
346                vfat_cluster_t *next_cluster)
347{
348        error_t err;
349        vfat_cluster_t next;
350
351        RCPC(ctx->fat_cid, RPC_PRIO_FS, __vfat_extend_cluster, 
352                                        RPC_RECV(RPC_RECV_OBJ(err), 
353                                                RPC_RECV_OBJ(next)),
354                                        RPC_SEND(RPC_SEND_OBJ(ctx), 
355                                                RPC_SEND_OBJ(current_vfat_cluster)));
356
357        *next_cluster=next;
358
359        return err;
360}
361
362
363RPC_DECLARE(__vfat_free_fat_entry, RPC_RET(RPC_RET_PTR(error_t, err)), 
364                        RPC_ARG(RPC_ARG_VAL(struct vfat_context_s*, ctx),
365                        RPC_ARG_VAL(vfat_cluster_t, start_cluster)))
366{
367        struct page_s *page;
368        struct mapper_s *mapper;
369        vfat_sector_t *sector;
370        vfat_cluster_t current_index;
371        vfat_cluster_t next_index;
372        vfat_sector_t lba;
373        size_t sector_size;
374        uint32_t sectors_per_page;
375
376        sector_size = ctx->bytes_per_sector;
377        sectors_per_page = PMM_PAGE_SIZE / sector_size;
378        current_index = start_cluster;
379        mapper = ctx->mapper;
380
381        vfat_dmsg(1,"%s: freeling fat entries starting by %u\n",  __FUNCTION__, start_cluster);
382
383        while((current_index > 0) && (current_index < 0x0FFFFFF7))
384        {
385                lba =  ctx->fat_begin_lba + ((current_index *4) / sector_size);
386
387                vfat_dmsg(1,"%s: query FAT at %d lba to get next cluster index of %u\n",
388                          __FUNCTION__, lba, current_index);
389
390                page = mapper_get_page(mapper, lba / sectors_per_page, MAPPER_SYNC_OP);
391
392                if(page == NULL)
393                {
394                        *err= VFS_IO_ERR;
395                        return;
396                }
397
398                sector = (vfat_sector_t*) ppm_page2addr(page);
399                sector += (lba%sectors_per_page) * (sector_size/4);
400
401                page_lock(page);
402                next_index = sector[current_index % (sector_size/4)] & 0x0FFFFFFF;
403                sector[current_index % (sector_size/4)] = 0x00;
404                mapper->m_ops->set_page_dirty(page);
405                page_unlock(page);
406                vfat_dmsg(1,"%s: page #%d (@%x) is set to delayed write\n", __FUNCTION__, page->index, page);
407                //page_refcount_down(page);
408
409#if VFAT_INSTRUMENT
410                wr_count ++;
411#endif
412
413                vfat_dmsg(1,"%s: content of %d is set to 0, next cluster in the list is %d\n",
414                                __FUNCTION__, current_index, next_index);
415                current_index = next_index;
416        }
417        *err = 0;
418}
419
420
421error_t vfat_free_fat_entry(struct vfat_context_s* ctx, vfat_cluster_t start_cluster)
422{
423        error_t err;
424        RCPC(ctx->fat_cid, RPC_PRIO_FS, __vfat_free_fat_entry, 
425                                        RPC_RECV(RPC_RECV_OBJ(err)), 
426                                        RPC_SEND(RPC_SEND_OBJ(ctx),
427                                        RPC_SEND_OBJ(start_cluster)));
428        return err;
429}
430
431//RPC_DECLARE(__vfat_locate_entry, RPC_RET(RPC_RET_PTR(error_t, err), RPC_RET_PTR(uint_t, entry_index)),
432//      RPC_ARG(RPC_ARG_PTR(char, entry_name), RPC_ARG_PTR(struct vfat_entry_request_s, rq)))
433error_t vfat_locate_entry(struct vfat_entry_request_s *rq)
434{
435        struct vfat_context_s *ctx;
436        struct vfat_DirEntry_s *dir;
437        struct page_s *page;
438        struct mapper_s *mapper;
439        struct vfs_inode_s *parent;
440        struct vfat_inode_s *parent_info;
441        uint_t entries_nr;
442        uint_t entry;
443        uint_t entry_id;
444        uint_t current_page;
445        size_t current_offset;
446        char name[VFS_MAX_NAME_LENGTH + 1]; /* +1 for string null termination */
447        uint32_t found;
448
449        ctx = rq->ctx;
450        dir = NULL;
451        page = NULL;
452        parent = rq->parent;
453        mapper = parent->i_mapper;
454        parent_info = parent->i_pv;
455        entries_nr = PMM_PAGE_SIZE / sizeof(struct vfat_DirEntry_s);
456        current_page = 0;
457        current_offset = 0;
458        found = 0;
459        entry_id = 0;
460
461        vfat_dmsg(1,"%s: started, node %s\n", __FUNCTION__, rq->entry_name);
462
463        while(!found) 
464        {
465                page = mapper_get_page(mapper, current_page, MAPPER_SYNC_OP);
466
467                if(page == NULL){ return VFS_IO_ERR;}
468
469                page_lock(page);
470
471                dir = (struct vfat_DirEntry_s*) ppm_page2addr(page);
472
473                for(entry=0; entry < entries_nr; entry ++)
474                {
475                        if(dir[entry].DIR_Name[0] == 0x00)
476                        {
477                                //page_refcount_down(page);
478                                page_unlock(page);
479                                return VFS_NOT_FOUND;
480                        }
481
482                        if(dir[entry].DIR_Name[0] == 0xE5)
483                                continue;
484
485                        if(dir[entry].DIR_Name[0] == '.')
486                                continue;
487
488                        if(rq->entry_name == NULL)
489                        {
490                                page_unlock(page);
491                                return VFS_ENOTEMPTY;
492                        }
493
494                        if(dir[entry].DIR_Attr == 0x0F)
495                                continue;
496
497                        vfat_getshortname((char *)dir[entry].DIR_Name, name);
498
499                        if(!strcmp(name,rq->entry_name))
500                        {
501                                found = 1;
502
503                                if (rq->entry != NULL)
504                                        memcpy(rq->entry, &dir[entry], sizeof(*dir));
505
506                                vfat_dmsg(1, "%s: entries_nr %d, entry_id %d, entry %d\n",
507                                                __FUNCTION__,
508                                                entries_nr,
509                                                entry_id,
510                                                entry);
511
512                                *rq->entry_index = (entry_id * entries_nr) + entry;
513                                break;
514                        }
515
516                        current_offset += sizeof (struct vfat_DirEntry_s);
517                }
518
519                entry_id++;
520                current_page++;
521                page_unlock(page);
522        }
523
524        vfat_dmsg(1,"%s: found %d, entry_index %d\n", __FUNCTION__, found, *rq->entry_index);
525
526        return (found) ? VFS_FOUND : VFS_NOT_FOUND;
527}
528
529
530RPC_DECLARE(__vfat_cluster_lookup, RPC_RET(RPC_RET_PTR(error_t, err), 
531                        RPC_RET_PTR(vfat_cluster_t, cluster_index),
532                        RPC_RET_PTR(uint_t, extended)),
533
534                        RPC_ARG(RPC_ARG_VAL(struct vfat_context_s*, ctx),
535                        RPC_ARG_VAL(vfat_cluster_t, node_cluster),
536                        RPC_ARG_VAL(vfat_cluster_t, cluster_rank)))
537                       
538{
539        struct page_s* page;
540        struct mapper_s* mapper;
541        register vfat_cluster_t i, current_vfat_cluster;
542        vfat_cluster_t next_cluster;
543        vfat_cluster_t cluster_offset, old_cluster_offset;
544        vfat_sector_t *sector;
545        uint32_t sectors_per_page;
546        uint32_t old_page_id, page_id;
547
548        sectors_per_page = PMM_PAGE_SIZE / ctx->bytes_per_cluster;
549        *extended = 0;
550        current_vfat_cluster = node_cluster;
551        next_cluster = 0;
552
553        old_cluster_offset = ctx->fat_begin_lba*(ctx->bytes_per_sector/4) + current_vfat_cluster;
554        old_page_id = old_cluster_offset / (PMM_PAGE_SIZE/4);
555
556        mapper = ctx->mapper;
557        page = mapper_get_page(mapper, old_page_id, MAPPER_SYNC_OP);
558
559        if(page == NULL) {*err = VFS_IO_ERR; return;}
560
561        sector = (vfat_sector_t*) ppm_page2addr(page);
562
563        vfat_dmsg(1,"%s: started, first cluster %u, cluster rank %d\n",__FUNCTION__, node_cluster, cluster_rank);
564
565        for(i=0; i < cluster_rank; i++) 
566        {
567                vfat_dmsg(5,"%s: loop %d/%d\n",__FUNCTION__,i+1,cluster_rank);
568                next_cluster = sector[old_cluster_offset % (PMM_PAGE_SIZE/4)] & 0x0FFFFFFF;
569                vfat_dmsg(5,"%s: next cluster found %u\n",__FUNCTION__, next_cluster);
570
571                if(next_cluster == 0x0FFFFFF7)  // bad block
572                        {*err = VFS_EBADBLK; return;}
573
574                if(next_cluster >= 0x0FFFFFF8)
575                { 
576                        if(vfat_extend_cluster(ctx, current_vfat_cluster, &next_cluster))
577                                // error while trying to extend
578                                {*err = VFS_IO_ERR; return;}
579
580                        if(next_cluster == 0)
581                                // no more space for another cluster
582                                {*err = VFS_ENOSPC; return;}
583
584                        *extended = 1;
585                }
586
587                current_vfat_cluster = next_cluster;
588                cluster_offset = ctx->fat_begin_lba * (ctx->bytes_per_sector/4) + current_vfat_cluster;
589                page_id = cluster_offset / (PMM_PAGE_SIZE/4);
590
591                if(page_id != old_page_id)
592                {
593                        //page_refcount_down(page);
594
595                        page = mapper_get_page(mapper, page_id, MAPPER_SYNC_OP);
596
597                        if(page == NULL) {*err = VFS_IO_ERR; return;}
598
599                        sector = (vfat_sector_t*) ppm_page2addr(page);
600
601                        vfat_dmsg(3, "%s: loading page %d for cluster %u\n", __FUNCTION__, page_id, next_cluster);
602                        old_page_id = page_id;
603                }
604
605                old_cluster_offset = cluster_offset;
606        }
607
608        vfat_dmsg(1, "%s: cluster found: %u\n", __FUNCTION__, current_vfat_cluster);
609        //page_refcount_down(page);
610        *cluster_index = current_vfat_cluster;
611        *err = 0;
612}
613
614error_t vfat_cluster_lookup(struct vfat_context_s* ctx,
615                            vfat_cluster_t node_cluster,
616                            vfat_cluster_t cluster_rank,
617                            vfat_cluster_t *cluster_index,
618                            uint_t *extended)
619{
620        error_t err;
621        vfat_cluster_t index;
622        uint_t extd;
623
624        RCPC(ctx->fat_cid, RPC_PRIO_FS, __vfat_cluster_lookup, 
625                                        RPC_RECV(RPC_RECV_OBJ(err), 
626                                                RPC_RECV_OBJ(index),
627                                                RPC_RECV_OBJ(extd)), 
628
629                                        RPC_SEND(RPC_SEND_OBJ(ctx),
630                                                RPC_SEND_OBJ(node_cluster), 
631                                                RPC_SEND_OBJ(cluster_rank)));
632        *extended = extd;
633        *cluster_index = index;
634        return err;
635}
Note: See TracBrowser for help on using the repository browser.