#define _POSIX_C_SOURCE 200809L #include #include #include #include "fileQueue.h" #include "util.h" #define MAXNAMELEN 512 /* create a fileT file */ fileT* createFileT(char *f, int O_LOCK, int client, int open) { if(!f){ errno = EINVAL; return NULL; } fileT *file = malloc(sizeof(fileT)); if (file == NULL) { perror("createFileT: malloc"); return NULL; } file->O_LOCK = (O_LOCK == 0)? 0 : 1; file->owner = client; file->open = (open == 0)? 0 : 1; file->size = 0; file->valid = 0; file->filepath = calloc(strnlen(f, MAXNAMELEN)+1, sizeof(char)); if (file->filepath == NULL) { perror("createFileT: malloc"); destroyFile(file); return NULL; } strncpy(file->filepath, f, strnlen(f, MAXNAMELEN)); /* after a realloc is enough */ if ((file->data = calloc(1, sizeof(char))) == NULL) { perror("createFileT: calloc"); return NULL; } return file; } /* append on fileT */ int writeFileT(fileT *f, void *data, size_t size) { if(!f || !data || size < 0){ errno = EINVAL; return -1; } if(size == 0) { return 0; } if ((f->data = realloc(f->data, f->valid + size)) == NULL) { perror("writeFileT: realloc"); return -1; } memcpy((char*) f->data + f->valid, data, size); f->valid = f->valid + size; f->size = (f->valid>f->size)?f->valid:f->size; return 0; } /* destroy fileT */ void destroyFile(fileT *f) { if(!f) return; if(f->filepath) free(f->filepath); if(f->data) free(f->data); free(f); } // ----------------------------------------------------------------------------- /* create queue */ queueT* createQueue(size_t maxLen, size_t maxSize) { if(maxLen<0 || maxSize<0){ errno = EINVAL; return NULL; } queueT *q = malloc(sizeof(queueT)); if(q==NULL){ perror("createQueue: malloc"); return NULL; } /* create lock */ if (pthread_mutex_init(&q->m, NULL) != 0) { perror("createQueue: pthread_mutex_init"); destroyQueue(q); return NULL; } q->head = NULL; q->tail = NULL; q->maxLen = maxLen; q->len = 0; q->maxSize = maxSize; q->size = 0; return q; } /* insert into the queue */ int enqueue(queueT *q, fileT* data) { if(!q || !data){ errno = EINVAL; return -1; } LOCK_RETURN(&q->m, -1); /* begin me */ if (q->len == q->maxLen) { /* too many files */ errno = ENFILE; goto _end_enqueue; } if (q->size + data->size > q->maxSize) { errno = EFBIG; goto _end_enqueue; } /* insert the element at the end */ nodeT *newNode = malloc(sizeof(nodeT)); if (newNode == NULL) { perror("enqueue: malloc"); goto _end_enqueue; } newNode->data = data; newNode->next = NULL; nodeT *tmp = q->head; if (q->head == NULL) q->head = newNode; else { while (tmp->next) tmp = tmp->next; tmp->next = newNode; } q->tail = newNode; ++q->len; q->size += data->size; UNLOCK_RETURN(&q->m, -1); /* end me */ return 0; _end_enqueue: UNLOCK_RETURN(&q->m, -1); return -1; } /* dequeue one element */ fileT* dequeue(queueT *q) { if(!q) { errno = EINVAL; return NULL; } LOCK_RETURN(&q->m, NULL); /* begin me */ if (q->head == NULL || q->len == 0) { /* empty queue */ errno = ENOENT; goto _end_dequeue; } fileT *data = (q->head)->data; /* dequeue the first in the list */ nodeT *tmp = NULL; tmp = q->head; q->head = (q->head)->next; if (q->head == NULL) { /* queue becomes empty */ q->tail = NULL; } --q->len; q->size -= data->size; free(tmp); /* free of the node */ UNLOCK_RETURN(&q->m, NULL); /* end me */ return data; _end_dequeue: UNLOCK_RETURN(&q->m, NULL); return NULL; } /* dequeue until we have s free space and expand the file by s */ fileT ** dequeueN(queueT *q, char *filepath, size_t s) { if(!q) { errno = EINVAL; return NULL; } if(s<=0) { return NULL; } LOCK_RETURN(&q->m, NULL); /* begin me */ fileT **returnList = NULL; /* list of removed files */ if (q->head == NULL || q->len == 0) { /* empty queue */ errno = ENOENT; goto _end_dequeueN; } if(q->maxSize < s) { errno = EINVAL; goto _end_dequeueN; } /* try to find the file */ nodeT *tmp = q->head; /* if LRU keep track of previous */ #if ALGORITHM == 1 nodeT *prev = NULL; #endif while (tmp) { if (strcmp(filepath, (tmp->data)->filepath) == 0) { /* found */ break; } /* if LRU keep track of previous */ #if ALGORITHM == 1 prev = tmp; #endif tmp = tmp->next; } if(!tmp) { /* not found */ errno = ENOENT; goto _end_dequeueN; } nodeT* file = tmp; /* if LRU put the file at the end */ #if ALGORITHM == 1 if(prev==NULL && tmp->next==NULL) { q->head = tmp; q->tail = tmp; } else if(prev==NULL) { q->head = tmp->next; q->tail->next = tmp; q->tail = tmp; tmp->next = NULL; } else if(tmp->next!=NULL) { prev->next = tmp->next; q->tail->next = tmp; q->tail = tmp; tmp->next = NULL; } #endif /* ALGORITHM == 1 */ tmp = q->head; returnList = calloc(1, sizeof(*returnList)); if(!returnList) { perror("dequeueN: calloc"); goto _end_dequeueN; } int purged = 0; int addFileAtEnd = 0; while(q->size + s > q->maxSize) { if (q->head == NULL) { /* queue becomes empty */ q->tail = NULL; break; /* we eliminated everything so we must have enought space */ } if(q->head == file) { /* dont delete the file to expand */ q->head = q->head->next; addFileAtEnd = 1; } tmp = q->head; q->head = q->head->next; ++purged; returnList = realloc(returnList, purged * sizeof(fileT*)); if(!returnList) { perror("dequeueN: realloc"); goto _end_dequeueN; } returnList[purged-1] = tmp->data; --q->len; q->size -= tmp->data->size; free(tmp); /* free of the node */ } if(addFileAtEnd == 1) { /* if LRU, we know already that this code is unrechable * and that q->head must be NULL if rechable */ if(q->head == NULL) { q->head = file; q->head->next = NULL; } else { file->next = q->head; q->head = file; } } returnList = realloc(returnList, (purged+1) * sizeof(fileT*)); if(!returnList) { perror("dequeueN: realloc"); goto _end_dequeueN; } returnList[purged] = NULL; /* null terminated */ file->data->size += s; q->size += s; UNLOCK_RETURN(&q->m, NULL); /* end me */ return returnList; _end_dequeueN: UNLOCK_RETURN(&q->m, NULL); if(returnList) free(returnList); return NULL; } void voidDequeue(queueT *q) { if(!q) { errno = EINVAL; return; } LOCK(&q->m); /* begin me */ if (q->head == NULL || q->len == 0) { /* empty queue */ errno = ENOENT; goto _end_void_dequeue; } nodeT *tmp = NULL; tmp = q->head; q->head = (q->head)->next; if (q->head == NULL) { /* queue becomes empty */ q->tail = NULL; } --q->len; q->size -= (tmp->data)->size; destroyFile(tmp->data); /* free fileT */ free(tmp); /* free node */ UNLOCK(&q->m); /* end me */ return; _end_void_dequeue: UNLOCK(&q->m); return; } /* print queue */ int printQueue(FILE *stream, queueT *q) { if(!q || !stream) { errno = EINVAL; return -1; } LOCK_RETURN(&q->m, -1); /* begin me */ nodeT *tmp = q->head; fprintf(stream, "Lista file:\n"); fprintf(stream, "[Nome File] -> Dimensione in MB\n"); while (tmp!=NULL) { float res = ((float)(tmp->data)->size)/1000000; /* in MB */ // float res = ((float)(tmp->data)->valid)/1000000; /* in MB */ fprintf(stream, "[%s] -> %f MB\n", (tmp->data)->filepath, res); tmp = tmp->next; } UNLOCK_RETURN(&q->m, -1); /* end me */ return 0; } /* acquire lock */ int lockFileInQueue(queueT *q, char *filepath, int owner) { if(!q || !filepath) { errno = EINVAL; return -1; } LOCK_RETURN(&q->m, -1); /* begin me */ if (q->len == 0) { errno = ENOENT; goto _end_lock_file_queue; } /* find file */ nodeT *tmp = q->head; /* if LRU keep track of previous */ #if ALGORITHM == 1 nodeT *prev = NULL; #endif while (tmp) { if (strcmp(filepath, (tmp->data)->filepath) == 0) { /* found */ break; } /* if LRU keep track of previous */ #if ALGORITHM == 1 prev = tmp; #endif tmp = tmp->next; } if(!tmp) { /* not found */ errno = ENOENT; goto _end_lock_file_queue; } /* lock already aquired by another client */ if ((tmp->data)->O_LOCK && (tmp->data)->owner != owner) { errno = EPERM; goto _end_lock_file_queue; } /* aquire lock on the file */ (tmp->data)->O_LOCK = 1; (tmp->data)->owner = owner; /* if LRU put the file at the end */ #if ALGORITHM == 1 if(prev==NULL && tmp->next==NULL) { q->head = tmp; q->tail = tmp; } else if(prev==NULL) { q->head = tmp->next; q->tail->next = tmp; q->tail = tmp; tmp->next = NULL; } else if(tmp->next!=NULL) { prev->next = tmp->next; q->tail->next = tmp; q->tail = tmp; tmp->next = NULL; } #endif /* ALGORITHM == 1 */ UNLOCK_RETURN(&q->m, -1); /* end me */ return 0; _end_lock_file_queue: UNLOCK_RETURN(&q->m, -1); return -1; } int unlockFileInQueue(queueT *q, char *filepath, int owner) { if(!q || !filepath) { errno = EINVAL; return -1; } LOCK_RETURN(&q->m, -1); /* begin me */ if (q->len == 0) { errno = ENOENT; goto _end_unlock_file_queue; } /* find the file */ nodeT *tmp = q->head; while (tmp) { if (strcmp(filepath, (tmp->data)->filepath) == 0) { /* found */ break; } tmp = tmp->next; } if(!tmp) { /* not found */ errno = ENOENT; goto _end_unlock_file_queue; } if((tmp->data)->O_LOCK == 0) { /* no one has the lock */ UNLOCK_RETURN(&q->m, -1); return 0; } /* another client holds the lock */ if((tmp->data)->O_LOCK == 1 && (tmp->data)->owner != owner) { errno = EPERM; goto _end_unlock_file_queue; } (tmp->data)->O_LOCK = 0; UNLOCK_RETURN(&q->m, -1); /* end me */ return 0; _end_unlock_file_queue: UNLOCK_RETURN(&q->m, -1); return -1; } /* open file */ int openFileInQueue(queueT *q, char *filepath, int O_LOCK, int owner) { if(!q || !filepath) { errno = EINVAL; return -1; } LOCK_RETURN(&q->m, -1); /* begin me */ if (q->len == 0) { errno = ENOENT; goto _end_open_file_queue; } /* find the file */ nodeT *tmp = q->head; /* if LRU keep track of previous */ #if ALGORITHM == 1 nodeT *prev = NULL; #endif while (tmp) { if (strcmp(filepath, (tmp->data)->filepath) == 0) { /* found */ break; } /* if LRU keep track of previous */ #if ALGORITHM == 1 prev = tmp; #endif tmp = tmp->next; } if(!tmp) { /* not found */ errno = ENOENT; goto _end_open_file_queue; } /* lock aquired by another client */ if((tmp->data)->O_LOCK == 1 && (tmp->data)->owner != owner) { errno = EPERM; goto _end_open_file_queue; } (tmp->data)->open = 1; (tmp->data)->O_LOCK = (O_LOCK==0)?0:1; if(O_LOCK!=0) { (tmp->data)->owner = owner; } /* if LRU put the file at the end */ #if ALGORITHM == 1 if(prev==NULL && tmp->next==NULL) { q->head = tmp; q->tail = tmp; } else if(prev==NULL) { q->head = tmp->next; q->tail->next = tmp; q->tail = tmp; tmp->next = NULL; } else if(tmp->next!=NULL) { prev->next = tmp->next; q->tail->next = tmp; q->tail = tmp; tmp->next = NULL; } #endif /* ALGORITHM == 1 */ UNLOCK_RETURN(&q->m, -1); /* end me */ return 0; _end_open_file_queue: UNLOCK_RETURN(&q->m, -1); return -1; } /* close and relese lock */ int closeFileInQueue(queueT *q, char *filepath, int owner) { if(!q || !filepath) { errno = EINVAL; return -1; } LOCK_RETURN(&q->m, -1); /* begin me */ if (q->len == 0) { errno = ENOENT; goto _end_close_file_queue; } /* find file */ nodeT *tmp = q->head; while (tmp) { if (strcmp(filepath, (tmp->data)->filepath) == 0) { /* found */ break; } tmp = tmp->next; } if(!tmp) { /* not found */ errno = ENOENT; goto _end_close_file_queue; } /* lock aquired by another client */ if((tmp->data)->O_LOCK == 1 && (tmp->data)->owner != owner) { errno = EPERM; goto _end_close_file_queue; } (tmp->data)->open = 0; (tmp->data)->O_LOCK = 0; (tmp->data)->owner = owner; UNLOCK_RETURN(&q->m, -1); /* end me */ return 0; _end_close_file_queue: UNLOCK_RETURN(&q->m, -1); return -1; } int writeFileInQueue(queueT *q, char *filepath, void *data, size_t size, int owner) { if(!q || !filepath || !data) { errno = EINVAL; return -1; } if(size == 0) return 0; LOCK_RETURN(&q->m, -1); /* begin me */ if (q->len == 0) { /* empty queue */ errno = ENOENT; goto _end_write_file_queue; } /* find the file */ nodeT *tmp = q->head; while (tmp) { if (strcmp(filepath, (tmp->data)->filepath) == 0) { /* found */ break; } tmp = tmp->next; } if(!tmp) { /* not found */ errno = ENOENT; goto _end_write_file_queue; } if (q->size - tmp->data->size + size > q->maxSize) { /* not enough space */ errno = EFBIG; goto _end_write_file_queue; } if ((tmp->data)->open == 0 || ((tmp->data)->O_LOCK && (tmp->data)->owner != owner)) { /* file is closed or the client does not hold the lock */ errno = EPERM; goto _end_write_file_queue; } /* write */ if (((tmp->data)->data = realloc((tmp->data)->data, size)) == NULL) { perror("writeFileInQueue: realloc"); goto _end_write_file_queue; } memcpy((tmp->data)->data, data, size); q->size = (q->size) - ((tmp->data)->size) + size; (tmp->data)->valid = size; (tmp->data)->size = size; UNLOCK_RETURN(&q->m, -1); /* end me */ return 0; _end_write_file_queue: UNLOCK_RETURN(&q->m, -1); return -1; } int appendFileInQueue(queueT *q, char *filepath, void *data, size_t size, int owner) { if (!q || !filepath || !data) { errno = EINVAL; return -1; } if (size == 0) return 0; LOCK_RETURN(&q->m, -1); /* begin me */ if (q->len == 0) { /* empty queue */ errno = ENOENT; goto _end_append_file_queue; } /* find the file */ nodeT *tmp = q->head; while (tmp) { if (strcmp(filepath, (tmp->data)->filepath) == 0) { /* found */ break; } tmp = tmp->next; } if(!tmp) { /* not found */ errno = ENOENT; goto _end_append_file_queue; } if (q->size - tmp->data->size + size > q->maxSize) { /* not enough space */ errno = EFBIG; goto _end_append_file_queue; } if ((tmp->data)->open == 0 || ((tmp->data)->O_LOCK && (tmp->data)->owner != owner)) { /* file is closed or the client does not hold the lock */ errno = EPERM; goto _end_append_file_queue; } /* write */ if (((tmp->data)->data = realloc((tmp->data)->data, (tmp->data)->valid + size)) == NULL) { perror("appendFileInQueue: realloc"); goto _end_append_file_queue; } memcpy(((char *)(tmp->data)->data) + (tmp->data)->valid, data, size); /* memmove could be an alternative */ (tmp->data)->valid += size; q->size -= (tmp->data)->size; (tmp->data)->size = ((tmp->data)->size > (tmp->data)->valid) ? (tmp->data)->size : (tmp->data)->valid; q->size += (tmp->data)->size; UNLOCK_RETURN(&q->m, -1); /* end me */ return 0; _end_append_file_queue: UNLOCK_RETURN(&q->m, -1); return -1; } int removeFileFromQueue(queueT *q, char *filepath, int owner) { if(!q || !filepath) { errno = EINVAL; return -1; } LOCK_RETURN(&q->m, -1); /* begin me */ if (q->len == 0) { /* empty queue */ errno = ENOENT; goto _end_remove_file_queue; } /* find the file */ nodeT *tmp = q->head; nodeT *pre = q->head; while (tmp) { if (strcmp(filepath, (tmp->data)->filepath) == 0) { /* found */ break; } if(pre!=tmp) pre = pre->next; tmp = tmp->next; } if (!tmp) { /* not found */ errno = ENOENT; goto _end_remove_file_queue; } if (((tmp->data)->O_LOCK) && ((tmp->data)->owner != owner)) { /* lock not by the client */ errno = EPERM; goto _end_remove_file_queue; } if (tmp == pre) { /* file is the first */ q->head = tmp->next; if (tmp->next == NULL) { q->tail = tmp; } } else { pre->next = tmp->next; if (pre->next == NULL) { q->tail = pre; } } --q->len; q->size -= (tmp->data)->size; destroyFile(tmp->data); /* free file */ free(tmp); /* free node */ UNLOCK_RETURN(&q->m, -1); /* end me */ return 0; _end_remove_file_queue: UNLOCK_RETURN(&q->m, -1); return -1; } // ----------------------------------------------------------------------------- /* search for file and return a copy */ fileT* find(queueT *q, char *filepath) { if(!q || !filepath) { errno = EINVAL; return NULL; } LOCK_RETURN(&q->m, NULL); /* begin me */ if (q->len == 0) goto _end_find_in_queue; fileT *res = NULL; nodeT *tmp = q->head; /* if LRU keep track of previous */ #if ALGORITHM == 1 nodeT *prev = NULL; #endif while (tmp) { if (strcmp(filepath, (tmp->data)->filepath) == 0) { /* trovato */ break; } /* if LRU keep track of previous */ #if ALGORITHM == 1 prev = tmp; #endif tmp = tmp->next; } if(!tmp) goto _end_find_in_queue; /* create a new instance */ res = createFileT((tmp->data)->filepath, (tmp->data)->O_LOCK, (tmp->data)->owner, (tmp->data)->open); if (!res) { perror("find: createFileT"); goto _end_find_in_queue; } if (writeFileT(res, (tmp->data)->data, (tmp->data)->size) == -1) { perror("find: writeFileT"); destroyFile(res); goto _end_find_in_queue; } /* if LRU put the file at the end */ #if ALGORITHM == 1 if(prev==NULL && tmp->next==NULL) { q->head = tmp; q->tail = tmp; } else if(prev==NULL) { q->head = tmp->next; q->tail->next = tmp; q->tail = tmp; tmp->next = NULL; } else if(tmp->next!=NULL) { prev->next = tmp->next; q->tail->next = tmp; q->tail = tmp; tmp->next = NULL; } #endif /* ALGORITHM == 1 */ UNLOCK_RETURN(&q->m, NULL); /* end me */ return res; _end_find_in_queue: UNLOCK_RETURN(&q->m, NULL); return NULL; } /* return n files */ fileT ** request(queueT *q, int n) { if(!q) { errno = EINVAL; return NULL; } if(n<=0) { return NULL; } LOCK_RETURN(&q->m, NULL); /* begin me */ fileT **returnList = NULL; /* list of requested files */ if (q->head == NULL || q->len == 0) { /* empty queue */ errno = ENOENT; goto _end_request; } if(q->size < n) { errno = EINVAL; goto _end_request; } returnList = calloc(n+1, sizeof(*returnList)); if(!returnList) { perror("dequeueN: calloc"); goto _end_request; } returnList[n] = NULL; nodeT *tmp = q->head; for(int i=0; idata; tmp = tmp->next; } /* if LRU put the files at the end */ #if ALGORITHM == 1 q->tail->next = q->head; q->head = tmp->next; tmp->next = NULL; q->tail = tmp; #endif /* ALGORITHM == 1 */ UNLOCK_RETURN(&q->m, NULL); /* end me */ return returnList; _end_request: UNLOCK_RETURN(&q->m, NULL); if(returnList) free(returnList); return NULL; } /* search for file and return result of search */ int searchFile(queueT *q, char *filepath) { if(!q || !filepath) { errno = EINVAL; return 0; } LOCK_RETURN(&q->m, 0); /* begin me */ if (q->len == 0) goto _end_search_file_in_queue; nodeT *tmp = q->head; /* if LRU keep track of previous */ #if ALGORITHM == 1 nodeT *prev = NULL; #endif while (tmp) { if (strcmp(filepath, (tmp->data)->filepath) == 0) { /* found */ break; } /* if LRU keep track of previous */ #if ALGORITHM == 1 prev = tmp; #endif tmp = tmp->next; } if(!tmp) goto _end_search_file_in_queue; /* if LRU put the file at the end */ #if ALGORITHM == 1 if(prev==NULL && tmp->next==NULL) { q->head = tmp; q->tail = tmp; } else if(prev==NULL) { q->head = tmp->next; q->tail->next = tmp; q->tail = tmp; tmp->next = NULL; } else if(tmp->next!=NULL) { prev->next = tmp->next; q->tail->next = tmp; q->tail = tmp; tmp->next = NULL; } #endif /* ALGORITHM == 1 */ UNLOCK_RETURN(&q->m, 0); /* end me */ return 1; _end_search_file_in_queue: UNLOCK_RETURN(&q->m, 0); return 0; } /* number of elements in queue */ size_t getLen(queueT *q) { if(!q) { errno = EINVAL; return -1; } size_t len = -1; LOCK_RETURN(&q->m, -1); /* begin me */ len = q->len; UNLOCK_RETURN(&q->m, -1); /* end me */ return len; } /* size in bytes of queue */ size_t getSize(queueT *q) { if(!q) { errno = EINVAL; return -1; } size_t size = -1; LOCK_RETURN(&q->m, -1); /* begin me */ size = q->size; UNLOCK_RETURN(&q->m, -1); /* end me */ return size; } void destroyQueue(queueT *q) { if(!q) return; /* no need for lock over queue */ while (q->len > 0) { errno = 0; voidDequeue(q); if (errno) { perror("destroyQueue: voidDequeue"); } if(q->head == NULL) { break; } } pthread_mutex_destroy(&q->m); free(q); }