Resolved race condition - this code is now considered as stable.
Reference: https://wiki.sei.cmu.edu/confluence/display/c/FIO24-C.+Do+not+open+a+file+that+is+already+open
This commit is contained in:
parent
bd2b43a58b
commit
cc1697894b
197
src/cache.c
197
src/cache.c
|
@ -110,10 +110,9 @@ void CacheSystem_init(const char *path)
|
||||||
*/
|
*/
|
||||||
static int Meta_read(Cache *cf)
|
static int Meta_read(Cache *cf)
|
||||||
{
|
{
|
||||||
FILE *fp;
|
FILE *fp = cf->mfp;
|
||||||
char *metafn = strndupcat(META_DIR, cf->path, MAX_PATH_LEN);
|
rewind(fp);
|
||||||
fp = fopen(metafn, "r");
|
|
||||||
free(metafn);
|
|
||||||
int res = 0;
|
int res = 0;
|
||||||
int nmemb = 0;
|
int nmemb = 0;
|
||||||
|
|
||||||
|
@ -137,8 +136,8 @@ static int Meta_read(Cache *cf)
|
||||||
/* These things really should not be zero!!! */
|
/* These things really should not be zero!!! */
|
||||||
if (!cf->content_length || !cf->blksz || !cf->segbc) {
|
if (!cf->content_length || !cf->blksz || !cf->segbc) {
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"Meta_read:() Warning corrupt metadata: content_length: %ld, \
|
"Meta_read(): corrupt metadata: %s, content_length: %ld, \
|
||||||
blksz: %d, segbc: %ld\n", cf->content_length, cf->blksz, cf->segbc);
|
blksz: %d, segbc: %ld\n", cf->path, cf->content_length, cf->blksz, cf->segbc);
|
||||||
res = EZERO;
|
res = EZERO;
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
@ -181,9 +180,6 @@ blksz: %d, segbc: %ld\n", cf->content_length, cf->blksz, cf->segbc);
|
||||||
}
|
}
|
||||||
|
|
||||||
end:
|
end:
|
||||||
if (fclose(fp)) {
|
|
||||||
fprintf(stderr, "Meta_read(): fclose(): %s\n", strerror(errno));
|
|
||||||
}
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -195,11 +191,9 @@ blksz: %d, segbc: %ld\n", cf->content_length, cf->blksz, cf->segbc);
|
||||||
*/
|
*/
|
||||||
static int Meta_write(Cache *cf)
|
static int Meta_write(Cache *cf)
|
||||||
{
|
{
|
||||||
FILE *fp;
|
|
||||||
char *metafn = strndupcat(META_DIR, cf->path, MAX_PATH_LEN);
|
|
||||||
fp = fopen(metafn, "w");
|
|
||||||
free(metafn);
|
|
||||||
int res = 0;
|
int res = 0;
|
||||||
|
FILE *fp = cf->mfp;
|
||||||
|
rewind(fp);
|
||||||
|
|
||||||
if (!fp) {
|
if (!fp) {
|
||||||
/* Cannot create the metadata file */
|
/* Cannot create the metadata file */
|
||||||
|
@ -207,10 +201,10 @@ static int Meta_write(Cache *cf)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* WARNING: These things really should not be zero!!! */
|
/* These things really should not be zero!!! */
|
||||||
if (!cf->content_length || !cf->blksz || !cf->segbc) {
|
if (!cf->content_length || !cf->blksz || !cf->segbc) {
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"Meta_write:() Warning: content_length: %ld, blksz: %d, segbc: \
|
"Meta_write(): Warning: content_length: %ld, blksz: %d, segbc: \
|
||||||
%ld\n", cf->content_length, cf->blksz, cf->segbc);
|
%ld\n", cf->content_length, cf->blksz, cf->segbc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -227,34 +221,9 @@ static int Meta_write(Cache *cf)
|
||||||
res = -1;
|
res = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fclose(fp)) {
|
|
||||||
fprintf(stderr, "Meta_write(): fclose(): %s\n", strerror(errno));
|
|
||||||
}
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief create a metadata file
|
|
||||||
* \details We set the followings here:
|
|
||||||
* - block size
|
|
||||||
* - the number of segments
|
|
||||||
*
|
|
||||||
* The number of segments depends on the block size. The block size is set to
|
|
||||||
* DATA_BLK_SZ for now. In future support for different block size may be
|
|
||||||
* implemented.
|
|
||||||
*/
|
|
||||||
static int Meta_create(Cache *cf)
|
|
||||||
{
|
|
||||||
cf->blksz = DATA_BLK_SZ;
|
|
||||||
cf->segbc = cf->content_length / cf->blksz + 1;
|
|
||||||
cf->seg = calloc(cf->segbc, sizeof(Seg));
|
|
||||||
if (!cf->seg) {
|
|
||||||
fprintf(stderr, "Meta_create(): calloc failure!\n");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
return Meta_write(cf);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief create a data file
|
* \brief create a data file
|
||||||
* \details We use sparse creation here
|
* \details We use sparse creation here
|
||||||
|
@ -439,11 +408,6 @@ static Cache *Cache_alloc()
|
||||||
"Cache_alloc(): rw_lock initialisation failed!\n");
|
"Cache_alloc(): rw_lock initialisation failed!\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pthread_mutex_init(&cf->meta_lock, NULL)) {
|
|
||||||
printf(
|
|
||||||
"Cache_alloc(): cf->meta_lock initialisation failed!\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
return cf;
|
return cf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -456,10 +420,6 @@ static void Cache_free(Cache *cf)
|
||||||
fprintf(stderr, "Cache_free(): could not destroy rw_lock!\n");
|
fprintf(stderr, "Cache_free(): could not destroy rw_lock!\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pthread_mutex_destroy(&cf->meta_lock)) {
|
|
||||||
fprintf(stderr, "Cache_free(): could not destroy meta_lock!\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cf->path) {
|
if (cf->path) {
|
||||||
free(cf->path);
|
free(cf->path);
|
||||||
}
|
}
|
||||||
|
@ -522,7 +482,7 @@ static int Cache_exist(const char *fn)
|
||||||
*/
|
*/
|
||||||
void Cache_delete(const char *fn)
|
void Cache_delete(const char *fn)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Cache_delete(): deleting %s\n", fn);
|
// fprintf(stderr, "Cache_delete(): deleting %s\n", fn);
|
||||||
char *metafn = strndupcat(META_DIR, fn, MAX_PATH_LEN);
|
char *metafn = strndupcat(META_DIR, fn, MAX_PATH_LEN);
|
||||||
char *datafn = strndupcat(DATA_DIR, fn, MAX_PATH_LEN);
|
char *datafn = strndupcat(DATA_DIR, fn, MAX_PATH_LEN);
|
||||||
if (!access(metafn, F_OK)) {
|
if (!access(metafn, F_OK)) {
|
||||||
|
@ -561,40 +521,96 @@ static int Data_open(Cache *cf)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Open a metafile
|
||||||
|
* \return
|
||||||
|
* - 0 on success
|
||||||
|
* - -1 on failure, with appropriate errno set.
|
||||||
|
*/
|
||||||
|
static int Meta_open(Cache *cf)
|
||||||
|
{
|
||||||
|
char *metafn = strndupcat(META_DIR, cf->path, MAX_PATH_LEN);
|
||||||
|
cf->mfp = fopen(metafn, "r+");
|
||||||
|
if (!cf->mfp) {
|
||||||
|
/* Failed to open the data file */
|
||||||
|
fprintf(stderr, "Meta_open(): fopen(%s): %s\n", metafn,
|
||||||
|
strerror(errno));
|
||||||
|
free(metafn);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
free(metafn);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Create a metafile
|
||||||
|
* \return
|
||||||
|
* - 0 on success
|
||||||
|
* - -1 on failure, with appropriate errno set.
|
||||||
|
*/
|
||||||
|
static int Meta_create(Cache *cf)
|
||||||
|
{
|
||||||
|
char *metafn = strndupcat(META_DIR, cf->path, MAX_PATH_LEN);
|
||||||
|
cf->mfp = fopen(metafn, "w");
|
||||||
|
if (!cf->mfp) {
|
||||||
|
/* Failed to open the data file */
|
||||||
|
fprintf(stderr, "Meta_create(): fopen(%s): %s\n", metafn,
|
||||||
|
strerror(errno));
|
||||||
|
free(metafn);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
free(metafn);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int Cache_create(Link *this_link)
|
int Cache_create(Link *this_link)
|
||||||
{
|
{
|
||||||
char *fn;
|
char *fn;
|
||||||
fn = curl_easy_unescape(
|
fn = curl_easy_unescape(NULL, this_link->f_url + ROOT_LINK_OFFSET, 0, NULL);
|
||||||
NULL, this_link->f_url + ROOT_LINK_OFFSET, 0, NULL);
|
|
||||||
if (Cache_exist(fn)) {
|
|
||||||
/* We make sure that the cache files are not outdated */
|
|
||||||
Cache *cf = Cache_open(fn);
|
|
||||||
if (cf->time == this_link->time) {
|
|
||||||
Cache_close(cf);
|
|
||||||
curl_free(fn);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
Cache_delete(fn);
|
|
||||||
}
|
|
||||||
|
|
||||||
fprintf(stderr, "Cache_create(): Creating cache files for %s.\n", fn);
|
fprintf(stderr, "Cache_create(): Creating cache files for %s.\n", fn);
|
||||||
|
|
||||||
Cache *cf = Cache_alloc();
|
Cache *cf = Cache_alloc();
|
||||||
cf->path = strndup(fn, MAX_PATH_LEN);
|
cf->path = strndup(fn, MAX_PATH_LEN);
|
||||||
cf->time = this_link->time;
|
cf->time = this_link->time;
|
||||||
cf->content_length = this_link->content_length;
|
cf->content_length = this_link->content_length;
|
||||||
|
cf->blksz = DATA_BLK_SZ;
|
||||||
|
cf->segbc = (cf->content_length / cf->blksz) + 1;
|
||||||
|
cf->seg = calloc(cf->segbc, sizeof(Seg));
|
||||||
|
|
||||||
|
if (!cf->seg) {
|
||||||
|
fprintf(stderr, "Cache_create(): cf->seg calloc failure!\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Meta_create(cf)) {
|
||||||
|
fprintf(stderr, "Cache_create(): cannot create metadata.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fclose(cf->mfp)) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"Cache_create(): cannot close metadata after creation: %s.\n",
|
||||||
|
strerror(errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Meta_open(cf)) {
|
||||||
|
Cache_free(cf);
|
||||||
|
fprintf(stderr, "Cache_create(): cannot open metadata file, %s.\n", fn);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Meta_write(cf)) {
|
||||||
|
fprintf(stderr, "Cache_create(): Meta_write() failed!\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fclose(cf->mfp)) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"Cache_create(): cannot close metadata after write, %s.\n",
|
||||||
|
strerror(errno));
|
||||||
|
}
|
||||||
|
|
||||||
if (Data_create(cf)) {
|
if (Data_create(cf)) {
|
||||||
fprintf(stderr, "Cache_create(): Data_create() failed!\n");
|
fprintf(stderr, "Cache_create(): Data_create() failed!\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
pthread_mutex_lock(&cf->meta_lock);
|
|
||||||
if (Meta_create(cf)) {
|
|
||||||
fprintf(stderr, "Cache_create(): Meta_create() failed!\n");
|
|
||||||
}
|
|
||||||
pthread_mutex_unlock(&cf->meta_lock);
|
|
||||||
|
|
||||||
|
|
||||||
Cache_free(cf);
|
Cache_free(cf);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -625,18 +641,20 @@ Cache *Cache_open(const char *fn)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Meta_open(cf)) {
|
||||||
|
Cache_free(cf);
|
||||||
|
fprintf(stderr, "Cache_open(): cannot open metadata file %s.\n", fn);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
pthread_mutex_lock(&cf->meta_lock);
|
|
||||||
int rtn = Meta_read(cf);
|
int rtn = Meta_read(cf);
|
||||||
pthread_mutex_unlock(&cf->meta_lock);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Internally inconsistent or corrupt metadata
|
* Internally inconsistent or corrupt metadata
|
||||||
*/
|
*/
|
||||||
if ((rtn == EINCONSIST) || (rtn == EZERO) || (rtn == EMEM)) {
|
if ((rtn == EINCONSIST) || (rtn == EZERO) || (rtn == EMEM)) {
|
||||||
Cache_free(cf);
|
Cache_free(cf);
|
||||||
fprintf(stderr, "Cache_open(): Opening cache file %s...", fn);
|
fprintf(stderr, "Cache_open(): metadata error: %s, %d.\n", fn, rtn);
|
||||||
fprintf(stderr, "metadata error!\n");
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -646,38 +664,43 @@ Cache *Cache_open(const char *fn)
|
||||||
* allocation policy.
|
* allocation policy.
|
||||||
*/
|
*/
|
||||||
if (cf->content_length > Data_size(fn)) {
|
if (cf->content_length > Data_size(fn)) {
|
||||||
fprintf(stderr, "Cache_open(): Opening cache file %s...", fn);
|
fprintf(stderr, "Cache_open(): metadata inconsistency %s, \
|
||||||
fprintf(stderr,
|
cf->content_length: %ld, Data_size(fn): %ld.\n", fn, cf->content_length,
|
||||||
"metadata inconsistency: cf->content_length: %ld, \
|
Data_size(fn));
|
||||||
Data_size(fn): %ld\n",
|
Cache_free(cf);
|
||||||
cf->content_length, Data_size(fn));
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if the cache files are not outdated */
|
||||||
|
if (cf->time != cf->link->time) {
|
||||||
|
fprintf(stderr, "Cache_open(): outdated cache file: %s.\n", fn);
|
||||||
Cache_free(cf);
|
Cache_free(cf);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Data_open(cf)) {
|
if (Data_open(cf)) {
|
||||||
Cache_free(cf);
|
Cache_free(cf);
|
||||||
fprintf(stderr, "Cache_open(): Opening cache file %s...", fn);
|
fprintf(stderr, "Cache_open(): cannot open data file %s.\n", fn);
|
||||||
fprintf(stderr, "Data_open() failed!\n");
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// fprintf(stderr, "Success!\n");
|
|
||||||
return cf;
|
return cf;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cache_close(Cache *cf)
|
void Cache_close(Cache *cf)
|
||||||
{
|
{
|
||||||
// fprintf(stderr, "Cache_close(): Closing cache file %s.\n", cf->path);
|
|
||||||
|
|
||||||
pthread_mutex_lock(&cf->meta_lock);
|
|
||||||
if (Meta_write(cf)) {
|
if (Meta_write(cf)) {
|
||||||
fprintf(stderr, "Cache_close(): Meta_write() error.");
|
fprintf(stderr, "Cache_close(): Meta_write() error.");
|
||||||
}
|
}
|
||||||
pthread_mutex_unlock(&cf->meta_lock);
|
|
||||||
|
if (fclose(cf->mfp)) {
|
||||||
|
fprintf(stderr, "Cache_close(): cannot close metadata: %s.\n",
|
||||||
|
strerror(errno));
|
||||||
|
}
|
||||||
|
|
||||||
if (fclose(cf->dfp)) {
|
if (fclose(cf->dfp)) {
|
||||||
fprintf(stderr, "Data_write(): fclose(): %s\n", strerror(errno));
|
fprintf(stderr, "Cache_close(): cannot close data file %s.\n",
|
||||||
|
strerror(errno));
|
||||||
}
|
}
|
||||||
|
|
||||||
return Cache_free(cf);
|
return Cache_free(cf);
|
||||||
|
|
|
@ -27,8 +27,8 @@ typedef struct {
|
||||||
long time; /**<the modified time of the file */
|
long time; /**<the modified time of the file */
|
||||||
off_t content_length; /**<the size of the file */
|
off_t content_length; /**<the size of the file */
|
||||||
pthread_mutex_t rw_lock; /**< mutex for disk operation */
|
pthread_mutex_t rw_lock; /**< mutex for disk operation */
|
||||||
pthread_mutex_t meta_lock; /**< mutex for metadata operation */
|
FILE *dfp; /**< The FILE pointer for the data file*/
|
||||||
FILE *dfp; /**< The FILE pointer for the cache data file*/
|
FILE *mfp; /**< The FILE pointer for the metadata */
|
||||||
Link *link; /**< The Link associated with this cache data set */
|
Link *link; /**< The Link associated with this cache data set */
|
||||||
int blksz; /**<the block size of the data file */
|
int blksz; /**<the block size of the data file */
|
||||||
long segbc; /**<segment array byte count */
|
long segbc; /**<segment array byte count */
|
||||||
|
|
Loading…
Reference in New Issue