2
Copyright (C) 2008- The University of Notre Dame
3
This software is distributed under the GNU General Public License.
4
See the file COPYING for details.
8
#include "chirp_alloc.h"
9
#include "chirp_protocol.h"
10
#include "chirp_filesystem.h"
13
#include "hash_table.h"
15
#include "int_sizes.h"
16
#include "stringtools.h"
18
#include "delete_dir.h"
29
static struct hash_table * alloc_table = 0;
30
static struct hash_table * root_table = 0;
31
static struct itable * fd_table = 0;
32
static int recovery_in_progress = 0;
33
static int alloc_enabled = 0;
44
Note that the space consumed by a file is not the same
45
as the filesize. This function computes the space consumed
46
by a file of a given size. Currently, it rounds up to
47
the next blocksize. A more exact function might take into
48
account indirect blocks allocated within the filesystem.
51
static INT64_T space_consumed( INT64_T filesize )
53
INT64_T block_size = 4096;
54
INT64_T blocks = filesize/block_size;
55
if(filesize%block_size) blocks++;
56
return blocks*block_size;
59
static void alloc_state_update( struct alloc_state *a, INT64_T change )
63
if(a->inuse<0) a->inuse=0;
64
a->avail = a->size-a->inuse;
69
static struct alloc_state * alloc_state_load( const char *path )
71
struct alloc_state *s = xxmalloc(sizeof(*s));
72
char statename[CHIRP_PATH_MAX];
74
debug(D_ALLOC,"locking %s",path);
76
sprintf(statename,"%s/.__alloc",path);
78
/* WARNING chirp_alloc assumes we are using the local filesystem */
79
s->file = fopen(statename,"r+");
85
if(lockf(fileno(s->file),F_TLOCK,0)) {
86
debug(D_ALLOC,"lock of %s blocked; flushing outstanding locks",path);
88
debug(D_ALLOC,"locking %s (retry)",path);
90
if(lockf(fileno(s->file),F_LOCK,0)) {
91
debug(D_ALLOC,"lock of %s failed: %s",path,strerror(errno));
98
fscanf(s->file,"%lld %lld",&s->size,&s->inuse);
102
if(recovery_in_progress) {
107
s->avail = s->size - s->inuse;
112
static void alloc_state_save( const char *path, struct alloc_state *s )
115
debug(D_ALLOC,"storing %s",path);
117
debug(D_ALLOC,"freeing %s",path);
121
ftruncate(fileno(s->file),0);
122
fseek(s->file,0,SEEK_SET);
123
fprintf(s->file,"%lld\n%lld\n",s->size,s->inuse);
129
static int alloc_state_create( const char *path, INT64_T size )
131
char statepath[CHIRP_PATH_MAX];
133
sprintf(statepath,"%s/.__alloc",path);
134
file = fopen(statepath,"w");
136
fprintf(file,"%lld 0\n",size);
144
static char * alloc_state_root( const char *path )
146
char dirname[CHIRP_PATH_MAX];
147
char statename[CHIRP_PATH_MAX];
150
strcpy(dirname,path);
153
sprintf(statename,"%s/.__alloc",dirname);
154
if(cfs->file_size(statename)>=0) {
155
return xstrdup(dirname);
157
s = strrchr(dirname,'/');
165
static char * alloc_state_root_cached( const char *path )
169
if(!root_table) root_table = hash_table_create(0,0);
171
result = hash_table_lookup(root_table,path);
172
if(result) return result;
174
result = alloc_state_root(path);
175
if(!result) return 0;
177
hash_table_insert(root_table,path,result);
182
static struct alloc_state * alloc_state_cache_exact( const char *path )
184
struct alloc_state *a;
186
char dirname[CHIRP_PATH_MAX];
187
char statename[CHIRP_PATH_MAX];
189
d = alloc_state_root_cached(path);
193
Save a copy of dirname, because the following
194
alloc_table_load_cached may result in a flush of the alloc table root.
199
sprintf(statename,"%s/.__alloc",dirname);
201
if(!alloc_table) alloc_table = hash_table_create(0,0);
203
a = hash_table_lookup(alloc_table,dirname);
206
a = alloc_state_load(dirname);
209
hash_table_insert(alloc_table,dirname,a);
214
static struct alloc_state * alloc_state_cache( const char *path )
216
char dirname[CHIRP_PATH_MAX];
217
string_dirname(path,dirname);
218
return alloc_state_cache_exact(dirname);
221
static void recover( const char *path )
223
char newpath[CHIRP_PATH_MAX];
224
struct chirp_stat info;
225
struct alloc_state *a, *b;
230
a = alloc_state_cache_exact(path);
231
if(!a) fatal("couldn't open alloc state in %s: %s",path,strerror(errno));
233
dir = cfs->opendir(path);
234
if(!dir) fatal("couldn't open %s: %s\n",path,strerror(errno));
236
while((d=cfs->readdir(dir))) {
237
if(!strcmp(d,".")) continue;
238
if(!strcmp(d,"..")) continue;
239
if(!strncmp(d,".__",3)) continue;
241
sprintf(newpath,"%s/%s",path,d);
243
result = cfs->lstat(newpath,&info);
244
if(result!=0) fatal("couldn't stat %s: %s\n",path,strerror(errno));
246
if(S_ISDIR(info.cst_mode)) {
248
b = alloc_state_cache_exact(newpath);
249
if(a!=b) alloc_state_update(a,b->size);
250
} else if(S_ISREG(info.cst_mode)) {
251
alloc_state_update(a,space_consumed(info.cst_size));
253
debug(D_ALLOC,"warning: unknown file type: %s\n",newpath);
259
debug(D_ALLOC,"%s (%sB)",path,string_metric(a->inuse,-1,0));
262
void chirp_alloc_init( const char *rootpath, INT64_T size )
264
struct alloc_state *a;
266
INT64_T inuse, avail;
268
#ifdef CCTOOLS_OPSYS_CYGWIN
269
fatal("sorry, CYGWIN cannot employ space allocation because it does not support file locking.");
273
recovery_in_progress = 1;
275
debug(D_ALLOC,"### begin allocation recovery scan ###");
277
if(!alloc_state_create(rootpath,size))
278
fatal("couldn't create allocation in %s: %s\n",rootpath,strerror(errno));
280
a = alloc_state_cache_exact(rootpath);
281
if(!a) fatal("couldn't find allocation in %s: %s\n",rootpath,strerror(errno));
292
debug(D_ALLOC,"### allocation recovery took %d seconds ###",stop-start);
294
debug(D_ALLOC,"%sB total",string_metric(size,-1,0));
295
debug(D_ALLOC,"%sB in use",string_metric(inuse,-1,0));
296
debug(D_ALLOC,"%sB available",string_metric(avail,-1,0));
298
recovery_in_progress = 0;
301
static time_t last_flush_time = 0;
303
void chirp_alloc_flush()
306
struct alloc_state *s;
308
if(!alloc_enabled) return;
310
debug(D_ALLOC,"flushing allocation states...");
312
if(!alloc_table) alloc_table = hash_table_create(0,0);
314
hash_table_firstkey(alloc_table);
315
while(hash_table_nextkey(alloc_table,&path,(void**)&s)) {
316
alloc_state_save(path,s);
317
hash_table_remove(alloc_table,path);
320
if(!root_table) root_table = hash_table_create(0,0);
322
hash_table_firstkey(root_table);
323
while(hash_table_nextkey(root_table,&path,(void**)&root)) {
325
hash_table_remove(root_table,path);
328
last_flush_time = time(0);
331
int chirp_alloc_flush_needed()
333
if(!alloc_enabled) return 0;
334
return hash_table_size(alloc_table);
337
time_t chirp_alloc_last_flush_time()
339
return last_flush_time;
342
INT64_T chirp_alloc_open( const char *path, INT64_T flags, INT64_T mode )
344
struct alloc_state *a;
347
if(!alloc_enabled) return cfs->open(path,flags,mode);
349
a = alloc_state_cache(path);
351
INT64_T filesize = cfs->file_size(path);
352
if(filesize<0) filesize = 0;
354
fd = cfs->open(path,flags,mode);
356
if(!fd_table) fd_table = itable_create(0);
357
itable_insert(fd_table,fd,xstrdup(path));
359
alloc_state_update(a,-space_consumed(filesize));
368
INT64_T chirp_alloc_close( int fd )
370
if(!alloc_enabled) return cfs->close(fd);
372
if(!fd_table) fd_table = itable_create(0);
373
char *path = itable_remove(fd_table,fd);
379
INT64_T chirp_alloc_pread( int fd, void *buffer, INT64_T length, INT64_T offset )
381
return cfs->pread(fd,buffer,length,offset);
384
INT64_T chirp_alloc_pwrite( int fd, const void *data, INT64_T length, INT64_T offset )
386
struct alloc_state *a;
389
if(!alloc_enabled) return cfs->pwrite(fd,data,length,offset);
391
if(!fd_table) fd_table = itable_create(0);
393
a = alloc_state_cache(itable_lookup(fd_table,fd));
395
INT64_T filesize = cfs->fd_size(fd);
397
INT64_T newfilesize = MAX(length+offset,filesize);
398
INT64_T alloc_change = space_consumed(newfilesize) - space_consumed(filesize);
399
if(a->avail>=alloc_change) {
400
result = cfs->pwrite(fd,data,length,offset);
401
if(result>0) alloc_state_update(a,alloc_change);
415
INT64_T chirp_alloc_sread( int fd, void *buffer, INT64_T length, INT64_T stride_length, INT64_T stride_skip, INT64_T offset )
417
return cfs->sread(fd,buffer,length,stride_length,stride_skip,offset);
420
INT64_T chirp_alloc_swrite( int fd, const void *buffer, INT64_T length, INT64_T stride_length, INT64_T stride_skip, INT64_T offset )
422
return cfs->swrite(fd,buffer,length,stride_length,stride_skip,offset);
425
INT64_T chirp_alloc_fstat( int fd, struct chirp_stat *buf )
427
return cfs->fstat(fd,buf);
430
INT64_T chirp_alloc_fstatfs( int fd, struct chirp_statfs *info )
432
struct alloc_state *a;
435
if(!alloc_enabled) return cfs->fstatfs(fd,info);
437
if(!fd_table) fd_table = itable_create(0);
439
a = alloc_state_cache(itable_lookup(fd_table,fd));
441
result = cfs->fstatfs(fd,info);
443
info->f_blocks = a->size/info->f_bsize;
444
info->f_bfree = a->avail/info->f_bsize;
445
info->f_bavail = a->avail/info->f_bsize;
455
INT64_T chirp_alloc_fchown( int fd, INT64_T uid, INT64_T gid )
457
return cfs->fchown(fd,uid,gid);
460
INT64_T chirp_alloc_fchmod( int fd, INT64_T mode )
462
return cfs->fchmod(fd,mode);
465
INT64_T chirp_alloc_ftruncate( int fd, INT64_T length )
467
struct alloc_state *a;
470
if(!alloc_enabled) return cfs->ftruncate(fd,length);
472
if(!fd_table) fd_table = itable_create(0);
474
a = alloc_state_cache(itable_lookup(fd_table,fd));
476
INT64_T filesize = cfs->fd_size(fd);
478
INT64_T alloc_change = space_consumed(length)-space_consumed(filesize);
479
if(a->avail>=alloc_change) {
480
result = cfs->ftruncate(fd,length);
481
if(result==0) alloc_state_update(a,alloc_change);
495
INT64_T chirp_alloc_fsync( int fd )
497
return cfs->fsync(fd);
500
void * chirp_alloc_opendir( const char *path )
502
return cfs->opendir(path);
505
char * chirp_alloc_readdir( void *dir )
507
return cfs->readdir(dir);
510
void chirp_alloc_closedir( void *dir )
512
return cfs->closedir(dir);
515
INT64_T chirp_alloc_getfile( const char *path, struct link *link, time_t stoptime )
517
return cfs->getfile(path,link,stoptime);
520
INT64_T chirp_alloc_getstream( const char *path, struct link *l, time_t stoptime )
522
INT64_T fd, result, actual, total = 0;
523
int buffer_size = 65536;
526
fd = chirp_alloc_open(path,O_RDONLY,0700);
529
link_putliteral(l,"0\n",stoptime);
531
buffer = malloc(buffer_size);
534
result = chirp_alloc_pread(fd,buffer,buffer_size,total);
537
actual = link_putlstring(l,buffer,result,stoptime);
538
if(actual!=result) break;
545
chirp_alloc_close(fd);
551
Note that putfile is given in advance the size of a file.
552
It checks the space available, and then guarantees that
553
the file will either be delivered whole or not at all.
556
INT64_T chirp_alloc_putfile( const char *path, struct link *link, INT64_T length, INT64_T mode, time_t stoptime )
558
struct alloc_state *a;
561
if(!alloc_enabled) return cfs->putfile(path,link,length,mode,stoptime);
563
result = chirp_alloc_unlink(path);
564
if(result<0 && errno!=ENOENT) return result;
566
a = alloc_state_cache(path);
568
if(a->avail>length) {
569
result = cfs->putfile(path,link,length,mode,stoptime);
571
alloc_state_update(a,space_consumed(result));
586
In contrast, putstream does not know the size of the output in advance,
587
and simply writes piece by piece, updating the allocation state as it goes.
590
INT64_T chirp_alloc_putstream( const char *path, struct link *l, time_t stoptime )
592
INT64_T fd, result, actual, total = 0;
593
int buffer_size = 65536;
596
fd = chirp_alloc_open(path,O_CREAT|O_TRUNC|O_WRONLY,0700);
599
link_putliteral(l,"0\n",stoptime);
601
buffer = malloc(buffer_size);
604
result = link_read(l,buffer,buffer_size,stoptime);
607
actual = chirp_alloc_pwrite(fd,buffer,result,total);
608
if(actual!=result) break;
615
chirp_alloc_close(fd);
620
INT64_T chirp_alloc_unlink( const char *path )
622
struct alloc_state *a;
625
if(!alloc_enabled) return cfs->unlink(path);
627
a = alloc_state_cache(path);
629
INT64_T filesize = cfs->file_size(path);
631
result = cfs->unlink(path);
632
if(result==0) alloc_state_update(a,-space_consumed(filesize));
642
INT64_T chirp_alloc_rename( const char *oldpath, const char *newpath )
644
struct alloc_state *a, *b;
647
if(!alloc_enabled) return cfs->rename(oldpath,newpath);
649
a = alloc_state_cache(oldpath);
651
b = alloc_state_cache(newpath);
654
result = rename(oldpath,newpath);
656
INT64_T filesize = cfs->file_size(oldpath);
658
if(b->avail>=filesize) {
659
result = cfs->rename(oldpath,newpath);
661
alloc_state_update(a,-space_consumed(filesize));
662
alloc_state_update(b,space_consumed(filesize));
682
INT64_T chirp_alloc_link( const char *path, const char *newpath )
684
if(!alloc_enabled) return cfs->link(path,newpath);
689
INT64_T chirp_alloc_symlink( const char *path, const char *newpath )
691
return cfs->symlink(path,newpath);
694
INT64_T chirp_alloc_readlink( const char *path, char *buf, INT64_T length )
696
return cfs->readlink(path,buf,length);
699
INT64_T chirp_alloc_mkdir( const char *path, INT64_T mode )
701
return cfs->mkdir(path,mode);
704
INT64_T chirp_alloc_rmall( const char *path )
708
result = chirp_alloc_unlink(path);
711
} else if(errno!=EISDIR) {
716
char subpath[CHIRP_PATH_MAX];
718
dir = chirp_alloc_opendir(path);
723
while((d=chirp_alloc_readdir(dir))) {
724
if(!strcmp(d,".")) continue;
725
if(!strcmp(d,"..")) continue;
726
if(!strncmp(d,".__ ",3)) continue;
727
sprintf(subpath,"%s/%s",path,d);
728
result = chirp_alloc_rmall(subpath);
732
chirp_alloc_closedir(dir);
735
return chirp_alloc_rmdir(path);
742
INT64_T chirp_alloc_rmdir( const char *path )
744
struct alloc_state *a, *d;
747
if(!alloc_enabled) return cfs->rmdir(path);
749
d = alloc_state_cache_exact(path);
751
a = alloc_state_cache(path);
753
if(cfs->rmdir(path)==0) {
755
alloc_state_update(a,-d->size);
756
debug(D_ALLOC,"rmalloc %s %lld",path,d->size);
768
INT64_T chirp_alloc_stat( const char *path, struct chirp_stat *buf )
770
return cfs->stat(path,buf);
773
INT64_T chirp_alloc_lstat( const char *path, struct chirp_stat *buf )
775
return cfs->lstat(path,buf);
778
INT64_T chirp_alloc_statfs( const char *path, struct chirp_statfs *info )
780
struct alloc_state *a;
783
if(!alloc_enabled) return cfs->statfs(path,info);
785
a = alloc_state_cache(path);
787
result = cfs->statfs(path,info);
789
info->f_blocks = a->size/info->f_bsize;
790
info->f_bavail = a->avail/info->f_bsize;
791
info->f_bfree = a->avail/info->f_bsize;
804
INT64_T chirp_alloc_mkfifo( const char *path )
806
return cfs->mkfifo(path);
809
INT64_T chirp_alloc_access( const char *path, INT64_T mode )
811
return cfs->access(path,mode);
814
INT64_T chirp_alloc_chmod( const char *path, INT64_T mode )
816
return cfs->chmod(path,mode);
819
INT64_T chirp_alloc_chown( const char *path, INT64_T uid, INT64_T gid )
821
return cfs->chown(path,uid,gid);
824
INT64_T chirp_alloc_lchown( const char *path, INT64_T uid, INT64_T gid )
826
return cfs->lchown(path,uid,gid);
829
INT64_T chirp_alloc_truncate( const char *path, INT64_T newsize )
831
struct alloc_state *a;
834
if(!alloc_enabled) return cfs->truncate(path,newsize);
836
a = alloc_state_cache(path);
838
INT64_T filesize = cfs->file_size(path);
840
INT64_T alloc_change = space_consumed(newsize)-space_consumed(filesize);
841
if(a->avail>=alloc_change) {
842
result = cfs->truncate(path,newsize);
843
if(result==0) alloc_state_update(a,alloc_change);
857
INT64_T chirp_alloc_utime( const char *path, time_t actime, time_t modtime )
859
return cfs->utime(path,actime,modtime);
862
INT64_T chirp_alloc_md5( const char *path, unsigned char digest[16] )
864
return cfs->md5(path,digest);
867
INT64_T chirp_alloc_lsalloc( const char *path, char *alloc_path, INT64_T *total, INT64_T *inuse )
870
struct alloc_state *a;
878
name = alloc_state_root_cached(path);
880
a = alloc_state_cache_exact(name);
882
strcpy(alloc_path,name);
895
INT64_T chirp_alloc_mkalloc( const char *path, INT64_T size, INT64_T mode )
897
struct alloc_state *a;
905
a = alloc_state_cache(path);
908
result = cfs->mkdir(path,mode);
910
if(alloc_state_create(path,size)) {
911
alloc_state_update(a,size);
912
debug(D_ALLOC,"mkalloc %s %lld",path,size);
929
INT64_T chirp_alloc_file_size( const char *path )
931
return cfs->file_size(path);
934
INT64_T chirp_alloc_fd_size( int fd )
936
return cfs->fd_size(fd);