~ubuntu-branches/ubuntu/vivid/cctools/vivid

« back to all changes in this revision

Viewing changes to parrot/src/pfs_service_grow.cc

  • Committer: Bazaar Package Importer
  • Author(s): Michael Hanke
  • Date: 2011-05-07 09:05:00 UTC
  • Revision ID: james.westby@ubuntu.com-20110507090500-lqpmdtwndor6e7os
Tags: upstream-3.3.2
ImportĀ upstreamĀ versionĀ 3.3.2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 
 
3
This module was written by Aureliano Rama and  Simone Pagan Griso,
 
4
and modified by Douglas Thain.  Igor Sfiligoi and Donatella Lucchesi
 
5
contributed significantly to the design and debugging of this system.
 
6
 
 
7
This software is distributed under the GNU General Public License.
 
8
See the file COPYING for details.
 
9
*/
 
10
 
 
11
/*
 
12
Theory of Operation:
 
13
 
 
14
The Global Read-Only Web (GROW) filesystem is designed to make a
 
15
directory tree stored on a web server accessible over the wide area,
 
16
with aggressive caching and end-to-end integrity checks.
 
17
 
 
18
To create a GROW filesystem, run make_growfs on the root of the
 
19
filesystem, and export it via a web server.  This script creates
 
20
a file .growfsdir that contains a complete directory listing and
 
21
checksum of all data.  Upon first accessing the filesystem remotely,
 
22
GROW-FS loads the directory listing into a tree form in memory.
 
23
All metadata requests and directory lookups are handled using this
 
24
data structure.
 
25
 
 
26
To access a file, GROW issues an HTTP request and reads the data
 
27
sequentially into the pfs_file_cache.  A checksum is computed
 
28
incrementally.  If the checksum does not match that in the directory
 
29
listing, the directory cache is discarded, and the close() fails
 
30
with EAGAIN, causing the pfs_file_cache to re-issue the open.
 
31
This procedure is repeated with an exponentially repeating backoff
 
32
until the filesystem becomes consistent.
 
33
 
 
34
The integrity of the directory listing is ensured by fetching
 
35
its checksum using https.  If the master checksum
 
36
and the directory listing are inconsistent, they are reloaded
 
37
in the same way as files.
 
38
 
 
39
This scheme is designed to maximize the cacheability of all components
 
40
of the filesystem.  Both files and data can be cached on local disk
 
41
without reomte consistency checks, as well as cached on shared proxy
 
42
servers, allowing the filesystem to scale to a very large number of clients.
 
43
 
 
44
(Note that GROW is designed to be an improvment over the old HTTP-FS
 
45
filesystem, which placed a listing in every single directory.)
 
46
 
 
47
*/
 
48
 
 
49
#include "pfs_service.h"
 
50
 
 
51
extern "C" {
 
52
#include "debug.h"
 
53
#include "stringtools.h"
 
54
#include "domain_name.h"
 
55
#include "link.h"
 
56
#include "file_cache.h"
 
57
#include "full_io.h"
 
58
#include "http_query.h"
 
59
#include "hash_table.h"
 
60
#include "xmalloc.h"
 
61
#include "macros.h"
 
62
#include "sha1.h"
 
63
#include "sleeptools.h"
 
64
}
 
65
 
 
66
#include <unistd.h>
 
67
#include <string.h>
 
68
#include <stdio.h>
 
69
#include <fcntl.h>
 
70
#include <errno.h>
 
71
#include <stdlib.h>
 
72
#include <ctype.h>
 
73
#include <sys/stat.h>
 
74
#include <sys/statfs.h>
 
75
 
 
76
#define GROW_LINE_MAX 4096
 
77
#define GROW_PORT 80
 
78
#define GROW_EPOCH 1199163600
 
79
 
 
80
extern int pfs_master_timeout;
 
81
extern int pfs_checksum_files;
 
82
extern char pfs_temp_dir[];
 
83
extern struct file_cache * pfs_file_cache;
 
84
 
 
85
extern void pfs_abort();
 
86
extern int pfs_cache_invalidate( pfs_name *name );
 
87
 
 
88
static struct grow_filesystem * grow_filesystem_list = 0;
 
89
static sha1_context_t grow_filesystem_checksum;
 
90
 
 
91
/*
 
92
A grow_filesystem structure represents an entire
 
93
filesystem rooted at a given host and path.
 
94
All known filesystem are kept in a linked list
 
95
rooted at grow_filesystem_list 
 
96
*/
 
97
 
 
98
struct grow_filesystem {
 
99
        char hostport[PFS_PATH_MAX];
 
100
        char path[PFS_PATH_MAX];
 
101
        struct grow_dirent *root;
 
102
        struct grow_filesystem *next;
 
103
};
 
104
 
 
105
/*
 
106
A grow_dirent is a node in a tree representing the
 
107
entire directory structure of a grow_filesystem.
 
108
Each node describes its name, metadata, checksum,
 
109
and children (if a directory)
 
110
*/
 
111
 
 
112
struct grow_dirent {
 
113
        char *name;
 
114
        char *linkname;
 
115
        unsigned mode;
 
116
        UINT64_T size;
 
117
        UINT64_T inode;
 
118
        time_t   mtime;
 
119
        char checksum[SHA1_DIGEST_ASCII_LENGTH];
 
120
        struct grow_dirent *children;
 
121
        struct grow_dirent *parent;
 
122
        struct grow_dirent *next;
 
123
};
 
124
 
 
125
void grow_dirent_delete( struct grow_dirent *d );
 
126
 
 
127
/*
 
128
Compare two path strings only up to the first slash.
 
129
For example, "foo" matches "foo/bar/baz".
 
130
Return one if they match, zero otherwise.
 
131
*/
 
132
 
 
133
static int compare_path_element( const char *a, const char *b )
 
134
{
 
135
        while(*a==*b) {
 
136
                if(!*a) return 1;
 
137
                if(*a=='/') return 1;
 
138
                a++;
 
139
                b++;
 
140
        }
 
141
 
 
142
        if(*a==0 && *b=='/') return 1;
 
143
        if(*b==0 && *a=='/') return 1;
 
144
 
 
145
        return 0;
 
146
}
 
147
 
 
148
/*
 
149
Compare two entire path strings to see if a is a prefix of b.
 
150
Return the remainder of b not matched by a.
 
151
For example, compare_path_prefix("foo/baz","foo/baz/bar") returns "/bar".
 
152
Return null if a is not a prefix of b.
 
153
*/
 
154
 
 
155
static const char * compare_path_prefix( const char *a, const char *b )
 
156
{
 
157
        while(1) {
 
158
                if(*a=='/' && *b=='/') {
 
159
                        while(*a=='/') a++;
 
160
                        while(*b=='/') b++;
 
161
                }
 
162
 
 
163
                if(!*a) return b;
 
164
                if(!*b) return 0;
 
165
 
 
166
                if(*a==*b) {
 
167
                        a++;
 
168
                        b++;
 
169
                        continue;
 
170
                } else {
 
171
                        return 0;
 
172
                }
 
173
        }
 
174
}
 
175
 
 
176
/*
 
177
Recursively create a grow directory structure by reading
 
178
descriptor lines from a stored file.
 
179
*/
 
180
 
 
181
 
 
182
struct grow_dirent * grow_dirent_create_from_file( FILE *file, struct grow_dirent *parent )
 
183
{
 
184
        struct grow_dirent *d;
 
185
        struct grow_dirent *list=0;
 
186
        char line[GROW_LINE_MAX];
 
187
        char name[GROW_LINE_MAX];
 
188
        char linkname[GROW_LINE_MAX];
 
189
        char type;
 
190
        static INT64_T inode=2;
 
191
        
 
192
        while(fgets(line,sizeof(line),file)) {
 
193
                sha1_update(&grow_filesystem_checksum,(unsigned char*)line,strlen(line));
 
194
                sha1_update(&grow_filesystem_checksum,(unsigned char*)"\n",1);
 
195
 
 
196
                if(line[0]=='E') break;
 
197
 
 
198
                d = (struct grow_dirent *) xxmalloc(sizeof(*d));
 
199
 
 
200
                linkname[0] = 0;
 
201
 
 
202
                /* old large file format */
 
203
                int fields = sscanf(line,"%c %[^\t]\t%d %*d %lld %*d %*d %ld %*d %s %[^\n]",
 
204
                        &type,
 
205
                        name,
 
206
                        &d->mode,
 
207
                        &d->size,
 
208
                        &d->mtime,
 
209
                        d->checksum,
 
210
                        linkname);
 
211
 
 
212
                if(fields<6) {
 
213
                  /* new more compact file format */
 
214
                fields = sscanf(line,"%c %[^\t]\t%u %llu %ld %s %[^\n]",
 
215
                        &type,
 
216
                        name,
 
217
                        &d->mode,
 
218
                        &d->size,
 
219
                        &d->mtime,
 
220
                        d->checksum,
 
221
                        linkname);
 
222
 
 
223
                        d->mtime += GROW_EPOCH;
 
224
                }
 
225
 
 
226
                d->inode = inode++;
 
227
 
 
228
                if(fields>=6) {
 
229
                        d->name = xstrdup(name);
 
230
                        if(linkname[0]) {
 
231
                                d->linkname = xstrdup(linkname);
 
232
                        } else {
 
233
                                d->linkname = 0;
 
234
                        }
 
235
                        if(type=='D') {
 
236
                                d->children = grow_dirent_create_from_file(file,d);
 
237
                        } else {
 
238
                                d->children = 0;
 
239
                        }
 
240
                        d->parent = parent;
 
241
                        d->next = list;
 
242
                        list = d;
 
243
                } else {
 
244
                        debug(D_GROW,"directory listing is corrupted!");
 
245
                        free(d);
 
246
                        grow_dirent_delete(list);
 
247
                        return 0;
 
248
                }
 
249
        }
 
250
 
 
251
        return list;
 
252
}
 
253
 
 
254
/*
 
255
Recursively destroy a directory structure.
 
256
*/
 
257
 
 
258
void grow_dirent_delete( struct grow_dirent *d )
 
259
{
 
260
        struct grow_dirent *n;
 
261
 
 
262
        while(d) {
 
263
                if(d->name) free(d->name);
 
264
                if(d->linkname) free(d->linkname);
 
265
                grow_dirent_delete(d->children);
 
266
                n = d->next;
 
267
                free(d);
 
268
                d = n;
 
269
        }
 
270
}
 
271
 
 
272
void grow_dirent_to_stat( struct grow_dirent *d, struct pfs_stat *s )
 
273
{
 
274
        s->st_dev = 1;
 
275
        s->st_ino = d->inode;
 
276
        s->st_mode = d->mode;
 
277
        s->st_nlink = 1;
 
278
        s->st_uid = 0;
 
279
        s->st_gid = 0;
 
280
        s->st_rdev = 1;
 
281
        s->st_size = d->size;
 
282
        s->st_blksize = 65536;
 
283
        s->st_blocks = 1+d->size/512;
 
284
        s->st_atime = d->mtime;
 
285
        s->st_mtime = d->mtime;
 
286
        s->st_ctime = d->mtime;
 
287
}
 
288
 
 
289
/*
 
290
Recursively search for the grow_dirent named by path
 
291
in the filesystem given by root.  If link_count is zero,
 
292
then do not traverse symbolic links.  Otherwise, when
 
293
link_count reaches 100, ELOOP is returned.
 
294
*/
 
295
 
 
296
 
 
297
struct grow_dirent * grow_dirent_lookup_recursive( const char *path, struct grow_dirent *root, int link_count )
 
298
{
 
299
        struct grow_dirent *d;
 
300
 
 
301
        if(!path) path = "\0";
 
302
        while(*path=='/') path++;
 
303
 
 
304
        if( S_ISLNK(root->mode) && ( link_count>0 || path[0] ) ) {
 
305
                if(link_count>100) {
 
306
                        errno = ELOOP;
 
307
                        return 0;
 
308
                }
 
309
 
 
310
                char *linkname = root->linkname;
 
311
 
 
312
                if(linkname[0]=='/') {
 
313
                        while(root->parent) {
 
314
                                root = root->parent;
 
315
                        }
 
316
                } else {
 
317
                        root = root->parent;
 
318
                }
 
319
 
 
320
                root = grow_dirent_lookup_recursive(linkname,root,link_count+1);
 
321
                if(!root) {
 
322
                        errno = ENOENT;
 
323
                        return 0;
 
324
                }
 
325
        }
 
326
 
 
327
        if(!*path) return root;
 
328
 
 
329
        if(!S_ISDIR(root->mode)) {
 
330
                errno = ENOTDIR;
 
331
                return 0;
 
332
        }
 
333
 
 
334
        const char *subpath = strchr(path,'/');
 
335
        if(!subpath) subpath = "\0";
 
336
 
 
337
        if(compare_path_element(".",path)) {
 
338
                return grow_dirent_lookup_recursive(subpath,root,link_count);
 
339
        }
 
340
 
 
341
        if(compare_path_element("..",path)) {
 
342
                if(root->parent) {
 
343
                        return grow_dirent_lookup_recursive(subpath,root->parent,link_count);
 
344
                } else {
 
345
                        errno = ENOENT;
 
346
                        return 0;
 
347
                }
 
348
        }
 
349
 
 
350
        for(d=root->children;d;d=d->next) {
 
351
                if(compare_path_element(d->name,path)) {
 
352
                        return grow_dirent_lookup_recursive(subpath,d,link_count);
 
353
                }
 
354
        }
 
355
 
 
356
        errno = ENOENT;
 
357
        return 0;
 
358
}
 
359
 
 
360
/*
 
361
Search for a grow filesystem rooted at the given host and path.
 
362
If the required files (.growfsdir and .growfschecksum) exist, then
 
363
create a grow filesystem struct and return it.  If the two
 
364
are not consistent, delay and loop until they are.
 
365
Otherwise, return zero.
 
366
*/
 
367
 
 
368
struct grow_filesystem * grow_filesystem_create( const char *hostport, const char *path )
 
369
{
 
370
        unsigned char digest[SHA1_DIGEST_LENGTH];
 
371
        unsigned char checksum[SHA1_DIGEST_ASCII_LENGTH];
 
372
        char line[GROW_LINE_MAX];
 
373
        char url[GROW_LINE_MAX];
 
374
        char filename[GROW_LINE_MAX];
 
375
        char txn[GROW_LINE_MAX];
 
376
        struct grow_filesystem *f;
 
377
        struct grow_dirent *d;
 
378
        FILE * file;
 
379
        struct link *link;
 
380
        int sleep_time = 1;
 
381
        time_t stoptime = time(0)+pfs_master_timeout;
 
382
 
 
383
        retry:
 
384
 
 
385
        sprintf(url,"http://%s%s/.growfschecksum",hostport,path);
 
386
 
 
387
        debug(D_GROW,"searching for filesystem at %s",url);
 
388
 
 
389
        debug(D_GROW,"fetching checksum: %s",url);
 
390
 
 
391
        link = http_query_no_cache(url,"GET",stoptime);
 
392
        if(link) {
 
393
                if(link_readline(link,line,sizeof(line),stoptime)) {
 
394
                        if(sscanf(line,"%s",checksum)) {
 
395
                                /* ok to continue */
 
396
                        } else {
 
397
                                debug(D_GROW,"checksum is malformed!");
 
398
                                goto sleep_retry;
 
399
                        }
 
400
                } else {
 
401
                        debug(D_GROW,"lost connection while fetching checksum!");
 
402
                        goto sleep_retry;
 
403
                }
 
404
        } else {
 
405
                return 0;
 
406
        }
 
407
 
 
408
        debug(D_GROW,"remote checksum is %s",checksum);
 
409
 
 
410
        sprintf(url,"http://%s%s/.growfsdir",hostport,path);
 
411
 
 
412
        if(file_cache_contains(pfs_file_cache,url,filename)!=0) {
 
413
 
 
414
                debug(D_GROW,"fetching directory: %s",url);
 
415
 
 
416
                int fd = file_cache_begin(pfs_file_cache,url,txn);
 
417
                if(fd>=0) {
 
418
                        INT64_T size;
 
419
                        struct link *link = http_query_size(url,"GET",&size,stoptime,1);
 
420
                        if(link) {
 
421
                                if(link_stream_to_fd(link,fd,size,stoptime)>=0) {
 
422
                                        file_cache_commit(pfs_file_cache,url,txn);
 
423
                                } else {
 
424
                                        file_cache_abort(pfs_file_cache,url,txn);
 
425
                                }
 
426
                                link_close(link);
 
427
                        } else {
 
428
                                file_cache_abort(pfs_file_cache,url,txn);
 
429
                        }
 
430
                        close(fd);
 
431
                }
 
432
        } else {
 
433
                debug(D_GROW,"directory is already cached");
 
434
        }
 
435
 
 
436
        if(file_cache_contains(pfs_file_cache,url,filename)!=0) {
 
437
                goto sleep_retry;
 
438
        }
 
439
 
 
440
        debug(D_GROW,"checksumming %s",filename);
 
441
 
 
442
        if(!sha1_file(filename,digest)) {
 
443
                debug(D_GROW,"couldn't checksum %s: %s",filename,strerror(errno));
 
444
                goto sleep_retry;
 
445
        }
 
446
        
 
447
        debug(D_GROW,"local checksum: %s",sha1_string(digest));
 
448
 
 
449
        if(strcmp((char*)checksum,sha1_string(digest))) {
 
450
                debug(D_GROW,"checksum does not match, reloading...");
 
451
                file_cache_delete(pfs_file_cache,url);
 
452
                goto sleep_retry;
 
453
        }
 
454
 
 
455
        file = fopen(filename,"r");
 
456
        if(!file) {
 
457
                debug(D_GROW,"couldn't open %s: %s",filename,strerror(errno));
 
458
                goto sleep_retry;
 
459
        }
 
460
 
 
461
        d = grow_dirent_create_from_file(file,0);
 
462
        if(!d) {
 
463
                debug(D_GROW,"%s is corrupted",filename);
 
464
                fclose(file);
 
465
                file_cache_delete(pfs_file_cache,url);
 
466
                goto sleep_retry;
 
467
        }
 
468
 
 
469
        fclose(file);
 
470
        
 
471
        f = (struct grow_filesystem *) malloc(sizeof(*f));
 
472
        strcpy(f->hostport,hostport);
 
473
        strcpy(f->path,path);
 
474
        f->root = d;
 
475
 
 
476
        return f;
 
477
 
 
478
        sleep_retry:
 
479
 
 
480
        if(sleep_time<pfs_master_timeout) {
 
481
                if(sleep_time>1) {
 
482
                        debug(D_GROW,"directory and checksum are inconsistent, retry in %d seconds",sleep_time);
 
483
                        sleep_for(sleep_time);
 
484
                }
 
485
                sleep_time*=2;
 
486
                goto retry;
 
487
        } else {
 
488
                fatal("directory and checksum still inconsistent after %d seconds",pfs_master_timeout);
 
489
                return 0;
 
490
        }
 
491
}
 
492
 
 
493
/*
 
494
Recursively destroy a grow filesystem.
 
495
*/
 
496
 
 
497
void grow_filesystem_delete( struct grow_filesystem *f )
 
498
{
 
499
        if(!f) return;
 
500
        grow_dirent_delete(f->root);
 
501
        grow_filesystem_delete(f->next);
 
502
        free(f);        
 
503
}
 
504
 
 
505
/*
 
506
Destroy all internal state for all filesystems.
 
507
This is called whenever a file checksum is found
 
508
to be inconsistent, and the state must be reloaded.
 
509
*/
 
510
 
 
511
void grow_filesystem_flush_all()
 
512
{
 
513
        grow_filesystem_delete(grow_filesystem_list);
 
514
        grow_filesystem_list = 0;
 
515
}
 
516
 
 
517
/*
 
518
Given a full PFS path name, search for an already-loaded
 
519
filesystem record.  If it exists, then search it for the
 
520
appropriate dirent.  If no filesystem record is found,
 
521
then search for and load the needed filesystem.
 
522
*/
 
523
 
 
524
struct grow_dirent * grow_dirent_lookup( pfs_name *name, int follow_links )
 
525
{
 
526
        struct grow_filesystem *f;
 
527
        char path[PFS_PATH_MAX];
 
528
        const char *subpath;
 
529
        char *s;
 
530
 
 
531
        for(f=grow_filesystem_list;f;f=f->next) {
 
532
                if(!strcmp(f->hostport,name->hostport)) {
 
533
                        subpath = compare_path_prefix(f->path,name->rest);
 
534
                        if(!subpath) {
 
535
                                subpath = compare_path_prefix(name->rest,f->path);
 
536
                                if(subpath) {
 
537
                                        errno = ENOENT;
 
538
                                        return 0;
 
539
                                } else {
 
540
                                        continue;
 
541
                                }
 
542
                        }
 
543
                        return grow_dirent_lookup_recursive(subpath,f->root,follow_links);
 
544
                }
 
545
        }
 
546
 
 
547
        strcpy(path,name->rest);
 
548
        while(1) {
 
549
                f = grow_filesystem_create(name->hostport,path);
 
550
                if(f) {
 
551
                        f->next = grow_filesystem_list;
 
552
                        grow_filesystem_list = f;
 
553
                        subpath = compare_path_prefix(f->path,name->rest);
 
554
                        return grow_dirent_lookup_recursive(subpath,f->root,follow_links);
 
555
                }
 
556
                s = strrchr(path,'/');
 
557
                if(s) {
 
558
                        *s = 0;
 
559
                } else {
 
560
                        break;
 
561
                }
 
562
        }
 
563
 
 
564
        errno = ENOENT;
 
565
        return 0;
 
566
}
 
567
 
 
568
class pfs_file_grow : public pfs_file
 
569
{
 
570
private:
 
571
        struct link *link;
 
572
        pfs_stat info;
 
573
        sha1_context_t context;
 
574
 
 
575
public:
 
576
        pfs_file_grow( pfs_name *n, struct link *l, struct grow_dirent *d ) : pfs_file(n) {
 
577
                link = l;
 
578
                grow_dirent_to_stat(d,&info);
 
579
                if(pfs_checksum_files) {
 
580
                        sha1_init(&context);
 
581
                }
 
582
        }
 
583
 
 
584
        virtual int close() {
 
585
                link_close(link);
 
586
 
 
587
                struct grow_dirent *d;
 
588
                d = grow_dirent_lookup(&name,1);
 
589
 
 
590
                if(!d) {
 
591
                        debug(D_GROW,"%s is no longer valid, will reload...",name);
 
592
                        grow_filesystem_flush_all();
 
593
                        errno = EAGAIN;
 
594
                        return -1;
 
595
                } else if(!strcmp(d->checksum,"0")) {
 
596
                        return 0;
 
597
                } else if(pfs_checksum_files) {
 
598
                        unsigned char digest[SHA1_DIGEST_LENGTH];
 
599
                        sha1_final(digest,&context);
 
600
                        if(!strcmp(sha1_string(digest),d->checksum)) {
 
601
                                return 0;
 
602
                        } else {
 
603
                                debug(D_GROW,"checksum failed on %s, will reload...",name.path);
 
604
                                grow_filesystem_flush_all();
 
605
                                errno = EAGAIN;
 
606
                                return -1;
 
607
                        }
 
608
                } else {
 
609
                        return 0;
 
610
                }
 
611
        }
 
612
 
 
613
        virtual pfs_ssize_t read( void *d, pfs_size_t length, pfs_off_t offset ) {
 
614
                pfs_ssize_t actual;
 
615
                actual = link_read(link,(char*)d,length,LINK_FOREVER);
 
616
                if(pfs_checksum_files && actual>0) sha1_update(&context,(unsigned char *)d,actual);
 
617
                return actual;
 
618
        }
 
619
 
 
620
        virtual int fstat( struct pfs_stat *i ) {
 
621
                *i = info;
 
622
                return 0;
 
623
        }
 
624
 
 
625
        /*
 
626
        This is a compatibility hack.
 
627
        This filesystem is read only, so locks make no sense.
 
628
        This simply satisfies some programs that insist upon it.
 
629
        */      
 
630
        virtual int flock( int op ) {
 
631
                return 0;
 
632
        }
 
633
 
 
634
        virtual pfs_ssize_t get_size() {
 
635
                return info.st_size;
 
636
        }
 
637
 
 
638
};
 
639
 
 
640
class pfs_service_grow : public pfs_service {
 
641
public:
 
642
        virtual int get_default_port() {
 
643
                return GROW_PORT;
 
644
        }
 
645
 
 
646
        virtual pfs_file * open( pfs_name *name, int flags, mode_t mode ) {
 
647
                struct grow_dirent *d;
 
648
                char url[PFS_PATH_MAX];
 
649
 
 
650
                d = grow_dirent_lookup(name,1);
 
651
                if(!d) return 0;
 
652
 
 
653
                if(S_ISDIR(d->mode)) {
 
654
                        errno = EISDIR;
 
655
                        return 0;
 
656
                }
 
657
 
 
658
                sprintf(url,"http://%s%s",name->hostport,name->rest);
 
659
 
 
660
                struct link *link = http_query_no_cache(url,"GET",time(0)+pfs_master_timeout);
 
661
                if(link) {
 
662
                        debug(D_GROW,"open %s",url);
 
663
                        return new pfs_file_grow(name,link,d);
 
664
                } else {
 
665
                        debug(D_GROW,"failed to open %s",url);
 
666
                        return 0;
 
667
                }
 
668
        }
 
669
 
 
670
        pfs_dir * getdir( pfs_name *name ) {
 
671
                struct grow_dirent *d;
 
672
 
 
673
                d = grow_dirent_lookup(name,1);
 
674
                if(!d) return 0;
 
675
 
 
676
                if(!S_ISDIR(d->mode)) {
 
677
                        errno = ENOTDIR;
 
678
                        return 0;
 
679
                }
 
680
 
 
681
                pfs_dir *dir = new pfs_dir(name);
 
682
 
 
683
                dir->append(".");
 
684
                if(d->parent) dir->append("..");
 
685
 
 
686
                for(d=d->children;d;d=d->next) {
 
687
                        dir->append(d->name);
 
688
                }
 
689
 
 
690
                return dir;
 
691
        }
 
692
 
 
693
        virtual int lstat( pfs_name *name, struct pfs_stat *info ) {
 
694
                struct grow_dirent *d;
 
695
 
 
696
                d = grow_dirent_lookup(name,0);
 
697
                if(!d) return -1;
 
698
 
 
699
                grow_dirent_to_stat(d,info);
 
700
 
 
701
                return 0;
 
702
        }
 
703
 
 
704
        virtual int stat( pfs_name *name, struct pfs_stat *info ) {
 
705
                struct grow_dirent *d;
 
706
 
 
707
                d = grow_dirent_lookup(name,1);
 
708
                if(!d) return -1;
 
709
 
 
710
                grow_dirent_to_stat(d,info);
 
711
 
 
712
                return 0;
 
713
        }
 
714
 
 
715
        virtual int unlink( pfs_name *name ) {
 
716
                errno = EROFS;
 
717
                return -1;
 
718
        }
 
719
 
 
720
        virtual int access( pfs_name *name, mode_t mode ) {
 
721
                struct pfs_stat info;
 
722
                if(this->stat(name,&info)==0) {
 
723
                        if(mode&W_OK) {
 
724
                                errno = EROFS;
 
725
                                return -1;
 
726
                        } else {
 
727
                                return 0;
 
728
                        }
 
729
                } else {
 
730
                        return -1;
 
731
                }
 
732
        }
 
733
 
 
734
        virtual int chmod( pfs_name *name, mode_t mode ) {
 
735
                errno = EROFS;
 
736
                return -1;
 
737
        }
 
738
 
 
739
        virtual int chown( pfs_name *name, uid_t uid, gid_t gid ) {
 
740
                errno = EROFS;
 
741
                return -1;
 
742
        }
 
743
 
 
744
        virtual int lchown( pfs_name *name, uid_t uid, gid_t gid ) {
 
745
                errno = EROFS;
 
746
                return -1;
 
747
        }
 
748
 
 
749
        virtual int truncate( pfs_name *name, pfs_off_t length ) {
 
750
                errno = EROFS;
 
751
                return -1;
 
752
        }
 
753
 
 
754
        virtual int utime( pfs_name *name, struct utimbuf *buf ) {
 
755
                errno = EROFS;
 
756
                return -1;
 
757
        }
 
758
 
 
759
        virtual int rename( pfs_name *oldname, pfs_name *newname ) {
 
760
                errno = EROFS;
 
761
                return -1;
 
762
        }
 
763
 
 
764
        virtual int chdir( pfs_name *name, char *newpath ) {
 
765
                struct pfs_stat info;
 
766
                if(this->stat(name,&info)==0) {
 
767
                        if(S_ISDIR(info.st_mode)) {
 
768
                                return 0;
 
769
                        } else {
 
770
                                errno = ENOTDIR;
 
771
                                return -1;
 
772
                        }
 
773
                } else {
 
774
                        return -1;
 
775
                }
 
776
        }
 
777
 
 
778
        virtual int link( pfs_name *oldname, pfs_name *newname ) {
 
779
                errno = EROFS;
 
780
                return -1;
 
781
        }
 
782
 
 
783
        virtual int symlink( const char *linkname, pfs_name *newname ) {
 
784
                errno = EROFS;
 
785
                return -1;
 
786
        }
 
787
 
 
788
        virtual int readlink( pfs_name *name, char *buf, pfs_size_t bufsiz ) {
 
789
                struct grow_dirent *d;
 
790
 
 
791
                d = grow_dirent_lookup(name,0);
 
792
                if(!d) return -1;
 
793
 
 
794
                if(S_ISLNK(d->mode)) {
 
795
                        int length;
 
796
                        strncpy(buf,d->linkname,bufsiz);
 
797
                        length = MIN((unsigned)bufsiz,strlen(d->linkname));
 
798
                        buf[length] = 0;
 
799
                        return length;
 
800
                } else {
 
801
                        errno = EINVAL;
 
802
                        return -1;
 
803
                }
 
804
        }
 
805
 
 
806
        virtual int mkdir( pfs_name *name, mode_t mode ) {
 
807
                errno = EROFS;
 
808
                return -1;
 
809
        }
 
810
 
 
811
        virtual int rmdir( pfs_name *name ) {
 
812
                errno = EROFS;
 
813
                return -1;
 
814
        }
 
815
};
 
816
 
 
817
static pfs_service_grow pfs_service_grow_instance;
 
818
pfs_service *pfs_service_grow = &pfs_service_grow_instance;
 
819
 
 
820