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:
Fufu Fang 2019-04-24 03:00:47 +01:00
parent bd2b43a58b
commit cc1697894b
2 changed files with 112 additions and 89 deletions

View File

@ -110,10 +110,9 @@ void CacheSystem_init(const char *path)
*/
static int Meta_read(Cache *cf)
{
FILE *fp;
char *metafn = strndupcat(META_DIR, cf->path, MAX_PATH_LEN);
fp = fopen(metafn, "r");
free(metafn);
FILE *fp = cf->mfp;
rewind(fp);
int res = 0;
int nmemb = 0;
@ -137,8 +136,8 @@ static int Meta_read(Cache *cf)
/* These things really should not be zero!!! */
if (!cf->content_length || !cf->blksz || !cf->segbc) {
fprintf(stderr,
"Meta_read:() Warning corrupt metadata: content_length: %ld, \
blksz: %d, segbc: %ld\n", cf->content_length, cf->blksz, cf->segbc);
"Meta_read(): corrupt metadata: %s, content_length: %ld, \
blksz: %d, segbc: %ld\n", cf->path, cf->content_length, cf->blksz, cf->segbc);
res = EZERO;
goto end;
}
@ -181,9 +180,6 @@ blksz: %d, segbc: %ld\n", cf->content_length, cf->blksz, cf->segbc);
}
end:
if (fclose(fp)) {
fprintf(stderr, "Meta_read(): fclose(): %s\n", strerror(errno));
}
return res;
}
@ -195,11 +191,9 @@ blksz: %d, segbc: %ld\n", cf->content_length, cf->blksz, cf->segbc);
*/
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;
FILE *fp = cf->mfp;
rewind(fp);
if (!fp) {
/* Cannot create the metadata file */
@ -207,10 +201,10 @@ static int Meta_write(Cache *cf)
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) {
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);
}
@ -227,34 +221,9 @@ static int Meta_write(Cache *cf)
res = -1;
}
if (fclose(fp)) {
fprintf(stderr, "Meta_write(): fclose(): %s\n", strerror(errno));
}
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
* \details We use sparse creation here
@ -439,11 +408,6 @@ static Cache *Cache_alloc()
"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;
}
@ -456,10 +420,6 @@ static void Cache_free(Cache *cf)
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) {
free(cf->path);
}
@ -522,7 +482,7 @@ static int Cache_exist(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 *datafn = strndupcat(DATA_DIR, fn, MAX_PATH_LEN);
if (!access(metafn, F_OK)) {
@ -561,40 +521,96 @@ static int Data_open(Cache *cf)
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)
{
char *fn;
fn = curl_easy_unescape(
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);
}
fn = curl_easy_unescape(NULL, this_link->f_url + ROOT_LINK_OFFSET, 0, NULL);
fprintf(stderr, "Cache_create(): Creating cache files for %s.\n", fn);
Cache *cf = Cache_alloc();
cf->path = strndup(fn, MAX_PATH_LEN);
cf->time = this_link->time;
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)) {
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);
/*
@ -625,18 +641,20 @@ Cache *Cache_open(const char *fn)
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);
pthread_mutex_unlock(&cf->meta_lock);
/*
* Internally inconsistent or corrupt metadata
*/
if ((rtn == EINCONSIST) || (rtn == EZERO) || (rtn == EMEM)) {
Cache_free(cf);
fprintf(stderr, "Cache_open(): Opening cache file %s...", fn);
fprintf(stderr, "metadata error!\n");
fprintf(stderr, "Cache_open(): metadata error: %s, %d.\n", fn, rtn);
return NULL;
}
@ -646,38 +664,43 @@ Cache *Cache_open(const char *fn)
* allocation policy.
*/
if (cf->content_length > Data_size(fn)) {
fprintf(stderr, "Cache_open(): Opening cache file %s...", fn);
fprintf(stderr,
"metadata inconsistency: cf->content_length: %ld, \
Data_size(fn): %ld\n",
cf->content_length, Data_size(fn));
fprintf(stderr, "Cache_open(): metadata inconsistency %s, \
cf->content_length: %ld, Data_size(fn): %ld.\n", fn, cf->content_length,
Data_size(fn));
Cache_free(cf);
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);
return NULL;
}
if (Data_open(cf)) {
Cache_free(cf);
fprintf(stderr, "Cache_open(): Opening cache file %s...", fn);
fprintf(stderr, "Data_open() failed!\n");
fprintf(stderr, "Cache_open(): cannot open data file %s.\n", fn);
return NULL;
}
// fprintf(stderr, "Success!\n");
return 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)) {
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)) {
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);

View File

@ -27,8 +27,8 @@ typedef struct {
long time; /**<the modified time of the file */
off_t content_length; /**<the size of the file */
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 cache data file*/
FILE *dfp; /**< The FILE pointer for the data file*/
FILE *mfp; /**< The FILE pointer for the metadata */
Link *link; /**< The Link associated with this cache data set */
int blksz; /**<the block size of the data file */
long segbc; /**<segment array byte count */