#define _POSIX_C_SOURCE 200809L #include #include #include #include "conn.h" #include "apiFile.h" #include "fileQueue.h" #include "taglialegna.h" #define MAXLENMESS 512 #define LOGBUFSIZE 2048 #define MEOK "20" /* OK */ // #define ME "21" /* not used */ // #define ME "22" /* not used */ #define MEFP "25" /* file purged */ // #define ME "40" /* not used */ // #define ME "41" /* not used */ #define MESD "42" /* shutting down */ #define MENT "45" /* requested file action not taken */ #define MESY "50" /* syntax error */ // #define ME "51" /* not used */ // #define ME "52" /* not used */ #define MESE "55" /* server error */ // ----------------------------------------------------------------------------- // helper functions /* send to the client the message m and then errno */ void serror(char *m, long fd_c, taglia_t *taglia, char *mlog) { if(!m) { errno = EINVAL; m = MESY; } if(writen(fd_c, m, strnlen(m, MAXLENMESS)) < 0) { perror("serror: writen"); goto _serror_cleanup; } if(writen(fd_c, &errno, sizeof(errno)) < 0) { perror("serror: writen"); goto _serror_cleanup; } if(taglia_write(taglia, mlog) < 0) goto _serror_cleanup; return; _serror_cleanup: return; } /* send to the client the message m */ void sendMessage(char *m, long fd_c, taglia_t *taglia, char *mlog) { if(!m) { m = MEOK; } if(writen(fd_c, m, strnlen(m, MAXLENMESS)) < 0) { perror("sendMessage: writen"); goto _sendM_cleanup; } if(taglia_write(taglia, mlog) < 0) goto _sendM_cleanup; return; _sendM_cleanup: return; } /* send to the client the file f */ int sendFile(fileT *f, long fd_c, taglia_t *taglia) { if(!f || !taglia) { errno = EINVAL; return -1; } /* send filepath dimension, then filepath */ int64_t filepathLength = (int64_t) strnlen(f->filepath, MAXLENMESS)+1; if (writen(fd_c, &filepathLength, sizeof(filepathLength)) < 0) { perror("sendFile: writen"); return -1; } if (writen(fd_c, f->filepath, (size_t) filepathLength) < 0) { perror("sendFile: writen"); return -1; } /* send file dimension, then file */ int64_t validLength = (int64_t) f->valid; if (writen(fd_c, &validLength, sizeof(validLength)) < 0) { perror("sendFile: writen"); return -1; } if(validLength != 0) { /* if file has length 0 dont write */ if (writen(fd_c, f->data, validLength) < 0) { perror("sendFile: writen"); return -1; } } char tmp_log[LOGBUFSIZE]; int n = 0; size_t m = sizeof(tmp_log); n += snprintf(tmp_log+n, m-n, "Inviato file \"%s\", di dimensione %"PRId64" Bytes al client %ld .\n", f->filepath, validLength, fd_c); if(taglia_write(taglia, tmp_log) < 0) { perror("sendFile: taglia_write"); return 1; } return 0; } /* send to the client the message m then the file f */ void sendMessageFile(char *m, fileT *f, long fd_c, taglia_t *taglia, char *mlog) { if(!f) { errno = EINVAL; char errmlog[2*LOGBUFSIZE] = "Errore negli argomenti alla funzione sendMessagefile (fileT == NULL). " "Messaggio originale:\n\t"; strncat(errmlog, mlog, sizeof(errmlog)-strlen(errmlog)-1); goto _sendMF_cleanup; serror(MESY, fd_c, taglia, errmlog); } if(!m) { m = MEFP; } if(writen(fd_c, m, strnlen(m, MAXLENMESS)) < 0) { perror("sendMessageFile: writen"); goto _sendMF_cleanup; } int64_t *n = calloc(1, sizeof(*n)); if(!n) { perror("sendMessageFile: calloc"); goto _sendMF_cleanup; } *n = 1; if(writen(fd_c, n, sizeof(int64_t)) < 0) { perror("sendMessageFile: writen"); goto _sendMF_cleanup; } free(n); if(sendFile(f, fd_c, taglia) < 0) { perror("sendMessageFile: sendFile"); goto _sendMF_cleanup; } if(taglia_write(taglia, mlog) < 0) { perror("sendMessageFile: taglia_write"); goto _sendMF_cleanup; } return; _sendMF_cleanup: return; } /* send to the client the message m then n files */ void sendMessageFileN(char *m, fileT **f, int64_t n, long fd_c, taglia_t *taglia, char *mlog) { if(!f) { errno = EINVAL; char errmlog[2*LOGBUFSIZE] = "Errore negli argomenti alla funzione sendMessagefile (fileT == NULL)." " Messaggio originale:\n\t"; strncat(errmlog, mlog, sizeof(errmlog)-strlen(errmlog)-1); serror(MESY, fd_c, taglia, errmlog); goto _sendMFN_cleanup; } if(!m) { m = MEFP; } if(writen(fd_c, m, strnlen(m, MAXLENMESS)) < 0) { perror("sendMessageFileN: writen"); goto _sendMFN_cleanup; } if(write(fd_c, &n, sizeof(n)) < 0) { perror("sendMessageFileN: writen"); goto _sendMFN_cleanup; } for(int i=0; i>1%2 */ fileT *removed = NULL; /* file that was removed */ if(found && create) { /* file found but want to create new file */ n += snprintf(tmp_buf+n, m-n, "Client %ld ha richiesto una openFile (flags = %x) sul file \"%s\" ma il file esiste già\n", fd_c, flags, filepath); errno = EEXIST; serror(MENT, fd_c, taglia, tmp_buf); return; } if(!found && !create) { /* file not found but expected */ n += snprintf(tmp_buf+n, m-n, "Client %ld ha richiesto una openFile (flags = %x) sul file \"%s\" ma il file non esiste\n", fd_c, flags, filepath); errno = ENOENT; serror(MENT, fd_c, taglia, tmp_buf); return; } if(found && !create) { /* file found */ if(openFileInQueue(q, filepath, lock, fd_c) == -1) { n += snprintf(tmp_buf+n, m-n, "Client %ld ha richiesto una openFile (flags = %x) sul file \"%s\" e' terminato con errore del server\n", fd_c, flags, filepath); perror("openFile: openFileInQueue"); serror(MESE, fd_c, taglia, tmp_buf); return; } n += snprintf(tmp_buf+n, m-n, "Client %ld ha richiesto una openFile (flags = %x) sul file \"%s\" e' terminata con successo\n", fd_c, flags, filepath); sendMessage(MEOK, fd_c, taglia, tmp_buf); return; } if(!found && create) { /* not found and creating new file */ if(getLen(q) == q->maxLen) { /* capacity miss */ removed = dequeue(q); if(!removed) { n += snprintf(tmp_buf+n, m-n, "Client %ld ha richiesto una openFile (flags = %x) sul file \"%s\" e' terminato con errore\n", fd_c, flags, filepath); perror("openFile: dequeue"); serror(MESE, fd_c, taglia, tmp_buf); return; } /* create new file */ fileT *f = createFileT(filepath, lock, fd_c, 1); if(!f) { n += snprintf(tmp_buf+n, m-n, "Client %ld ha richiesto una openFile (flags = %x) sul file \"%s\" e' terminato con errore del server\n", fd_c, flags, filepath); perror("openFile: createFileT"); serror(MESE, fd_c, taglia, tmp_buf); return; } if(enqueue(q, f) != 0) { n += snprintf(tmp_buf+n, m-n, "Client %ld ha richiesto una openFile (flags = %x) sul file \"%s\" e' terminato con errore del server\n", fd_c, flags, filepath); perror("openFile: enqueue"); serror(MESE, fd_c, taglia, tmp_buf); return; } taglia_update(taglia, q, 1); /* removed only one file */ n += snprintf(tmp_buf+n, m-n, "Client %ld ha richiesto una openFile (flags = %x) sul file \"%s\" ha causato una capacity miss. File espulso \"%s\"\n", fd_c, flags, filepath, removed->filepath); sendMessageFile(MEFP, removed, fd_c, taglia, tmp_buf); destroyFile(removed); return; } /* no capacity miss */ fileT *f = createFileT(filepath, lock, fd_c, 1); if(!f) { n += snprintf(tmp_buf+n, m-n, "Client %ld ha richiesto una openFile (flags = %x) sul file \"%s\" e' terminato con errore del server\n", fd_c, flags, filepath); perror("openFile: createFileT"); serror(MESE, fd_c, taglia, tmp_buf); return; } if(enqueue(q, f) != 0) { n += snprintf(tmp_buf+n, m-n, "Client %ld ha richiesto una openFile (flags = %x) sul file \"%s\" e' terminato con errore del server\n", fd_c, flags, filepath); perror("openFile: enqueue"); serror(MESE, fd_c, taglia, tmp_buf); return; } /* added a file, the number of files changed so update the log */ taglia_update(taglia, q, 0); n += snprintf(tmp_buf+n, m-n, "Client %ld ha richiesto una openFile (flags = %x) sul file \"%s\" e' terminato con successo\n", fd_c, flags, filepath); sendMessage(MEOK, fd_c, taglia, tmp_buf); return; } /* unreachable code */ return; } void readFile(char *filepath, queueT *q, long fd_c, taglia_t *taglia) { /* message to write to the logfile */ char tmp_buf[LOGBUFSIZE]; int n = 0; size_t m = sizeof(tmp_buf); if(!filepath || !q || !taglia) { n += snprintf(tmp_buf+n, m-n, "Client %ld ha richiesto una readFile sul file \"%s\" e' terminata con errore del server\n", fd_c, filepath); errno = EINVAL; serror(MESY, fd_c, taglia, tmp_buf); return; } fileT *f = NULL; f = find(q, filepath); if(!f) { n += snprintf(tmp_buf+n, m-n, "Client %ld ha richiesto una readFile sul file \"%s\" ma il file non esiste\n", fd_c, filepath); errno = ENOENT; serror(MESE, fd_c, taglia, tmp_buf); return; } if(f->open == 0) { /* file not already open */ n += snprintf(tmp_buf+n, m-n, "Client %ld ha richiesto una readFile sul file \"%s\" e' terminata con errore, file non precedentemente aperto\n", fd_c, filepath); errno = EPERM; serror(MENT, fd_c, taglia, tmp_buf); destroyFile(f); /* f is a copy so we need to cleen up */ return; } n += snprintf(tmp_buf+n, m-n, "Client %ld ha richiesto una readFile sul file \"%s\" di dimensione = %ld e' terminata con successo\n", fd_c, filepath, f->valid); sendMessageFile(MEOK, f, fd_c, taglia, tmp_buf); destroyFile(f); /* f is a copy so we need to cleen up */ return; } void readNFiles(int num, queueT *q, long fd_c, taglia_t *taglia) { /* message to write to the logfile */ char tmp_buf[LOGBUFSIZE]; int n = 0; size_t m = sizeof(tmp_buf); if(!q || !taglia) { n += snprintf(tmp_buf+n, m-n, "Client %ld ha richiesto una readNFile (n = %d) e' terminata con errore\n", fd_c, num); errno = EINVAL; serror(MESY, fd_c, taglia, tmp_buf); return; } int oldNum = num; int64_t ntosend = (num<=0 || num>getLen(q))? getLen(q) : num; fileT **toSend = NULL; n += snprintf(tmp_buf+n, m-n, "Client %ld ha richiesto una readNFile (n = %d) e' terminata con successo. File inviati:\n", fd_c, num); /* extract n files, but dont actually dequeue them */ toSend = request(q, (int) ntosend); if(toSend == NULL) { n = 0; n += snprintf(tmp_buf+n, m-n, "Client %ld ha richiesto una readNFiles (n = %d) e' terminato con errore del server\n", fd_c, oldNum); perror("readNFiles: request"); serror(MESE, fd_c, taglia, tmp_buf); return; } size_t tot = 0; for(int i=0;ivalid; n += snprintf(tmp_buf+n, m-n, "\t%d) \"%s\" dim = %ld\n", i, toSend[i]->filepath, toSend[i]->valid); } n += snprintf(tmp_buf+n, m-n, "readNFile dimensione totale = %ld B\n", tot); sendMessageFileN(MEOK, toSend, ntosend, fd_c, taglia, tmp_buf); free(toSend); return; } void writeFile(char *filepath, size_t size, queueT *q, long fd_c, taglia_t *taglia, int append) { /* message to write to the logfile */ char tmp_buf[LOGBUFSIZE]; int n = 0; size_t m = sizeof(tmp_buf); if(!filepath || !q || !taglia) { n += snprintf(tmp_buf+n, m-n, "Client %ld ha richiesto una writeFile (append = %x) sul file \"%s\" e' terminata con errore\n", fd_c, append, filepath); errno = EINVAL; serror(MESY, fd_c, taglia, tmp_buf); return; } /* search the file */ fileT *f = NULL; f = find(q, filepath); if(!f) { /* file is not present */ n += snprintf(tmp_buf+n, m-n, "Client %ld ha richiesto una writeFile (append = %x) sul file \"%s\", dimensione = %ld, e' terminata con errore, file non trovato\n", fd_c, append, filepath, size); errno = ENOENT; serror(MENT, fd_c, taglia, tmp_buf); destroyFile(f); return; } /* file not open || no append and no lock || not the owner of the lock */ if(!f->open || (!append && !f->O_LOCK) || (f->O_LOCK && f->owner != fd_c)) { n += snprintf(tmp_buf+n, m-n, "Client %ld ha richiesto una writeFile (append = %x) sul file \"%s\", dimensione = %ld, e' terminata con errore, non proprietario del file\n", fd_c, append, filepath, size); errno = EPERM; serror(MENT, fd_c, taglia, tmp_buf); destroyFile(f); return; } long trueSizeAdded = 0; /* we may have alredy some space allocated */ if(append) { trueSizeAdded = size - f->size + f->valid; } else { trueSizeAdded = (size>f->size)? size-f->size : 0; } destroyFile(f); /* not needed anymore */ if(trueSizeAdded > q->maxSize) { /* removing all files would not be enought */ n += snprintf(tmp_buf+n, m-n, "Client %ld ha richiesto una writeFile (append = %x) sul file \"%s\", dimensione = %ld ma il file e' piu' grande della dimensione massima\n", fd_c, append, filepath, size); errno = EFBIG; serror(MENT, fd_c, taglia, tmp_buf); return; } if(trueSizeAdded + getSize(q) > q->maxSize) { /* writing would be more than capacity */ fileT **removed = NULL; /* array that may (worst case) hold all files to * be sent to the client */ removed = dequeueN(q, filepath, trueSizeAdded); if(!removed) { /* internal error */ n += snprintf(tmp_buf+n, m-n, "Client %ld ha richiesto una writeFile (append = %x) sul file \"%s\", dimensione = %ld, e' terminata con errore del server\n", fd_c, append, filepath, size); errno = ENOENT; serror(MESY, fd_c, taglia, tmp_buf); return; } int ln = 0; fileT *tmp = removed[ln]; n += snprintf(tmp_buf+n, m-n, "Client %ld ha richiesto una writeFile (append = %x) sul file \"%s\", dimensione = %ld, e' terminata con successo, allocando %ld B in memoria. Ha causato una capacity miss e ha fatto espellere i seguenti file:", fd_c, append, filepath, size, trueSizeAdded); while(tmp!=NULL) { n += snprintf(tmp_buf+n, m-n, " \"%s\"", tmp->filepath); ++ln; tmp=removed[ln]; } n += snprintf(tmp_buf+n, m-n, "\n"); taglia_update(taglia, q, ln); sendMessageFileN(MEFP, removed, ln, fd_c, taglia, tmp_buf); for(int i=0;ifile = calloc(MAXLENMESS, sizeof(char)); if (!new->file) { perror("addWaiting: calloc"); free(new); return -1; } strncpy(new->file, filepath, strnlen(filepath, MAXLENMESS-1)+1); new->fd = fd_c; new->next = NULL; /* if the list is empty */ if(*waiting == NULL) { *waiting = new; return 0; } /* if the list has at least 1 element */ waiting_t *tail = *waiting; while(tail->next) { tail = tail->next; } tail->next = new; return 0; } /* remove first who is waiting on the lock for the file * if no one is waiting for the lock it returns -1 */ int removeFirstWaiting(waiting_t **waiting, char *filepath) { if(!waiting || !filepath) { errno = EINVAL; return -1; } /* none waiting */ if(*waiting == NULL) { return -1; } waiting_t *curr = *waiting; waiting_t *prec = NULL; long fd_c = -1; /* first one waiting */ if (strcmp(curr->file, filepath) == 0) { fd_c = curr->fd; *waiting = curr->next; free(curr->file); free(curr); return fd_c; } /* more than one waiting */ while (curr->next) { prec = curr; curr = curr->next; if (strcmp(curr->file, filepath) == 0) { fd_c = curr->fd; prec->next = curr->next; free(curr->file); free(curr); return fd_c; } } return -1; } /* free waiting structure */ void clearWaiting(waiting_t **waiting) { if(!waiting) { return; } waiting_t *curr = *waiting; waiting_t *next = NULL; while (curr) { next = curr->next; if(curr->file) free(curr->file); free(curr); curr = next; } *waiting = NULL; return; }