#define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include #define MAXLENMESS 512 #define LOGBUFSIZE 2048 #define MEOK "20" //OK #define MEHE "21" // help message // #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 // ----------------------------------------------------------------------------- /* funzioni ausiliarie */ // invio il messaggio al client e poi l'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)+1) < 0) { perror("writen"); goto _serror_cleanup; } if(writen(fd_c, &errno, sizeof(errno)) < 0) { perror("writen"); goto _serror_cleanup; } if(taglia_write(taglia, mlog) < 0) goto _serror_cleanup; return; _serror_cleanup: return; } // invio il messaggio al client void sendMessage(char *m, long fd_c, taglia_t *taglia, char *mlog) { if(!m) { m = MEOK; } if(writen(fd_c, m, strnlen(m, MAXLENMESS)+1) < 0) { perror("writen"); goto _sendM_cleanup; } if(taglia_write(taglia, mlog) < 0) goto _sendM_cleanup; return; _sendM_cleanup: return; } // invia un file 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("writen"); return -1; } if (writen(fd_c, f->filepath, (size_t) filepathLength) < 0) { perror("writen"); return -1; } // send file dimension, then file int64_t validLength = (int64_t) f->valid; if (writen(fd_c, &validLength, sizeof(validLength)) < 0) { perror("writen"); return -1; } if (writen(fd_c, f->data, f->valid) < 0) { perror("writen"); return -1; } char tmp_log[LOGBUFSIZE]; int n = 0; size_t m = sizeof(tmp_log); n += snprintf(tmp_log+n, m-n, "File \"%s\", di dimensione %"PRId64" Bytes al client %ld .\n", f->filepath, validLength, fd_c); if(taglia_write(taglia, tmp_log) < 0) { perror("taglia_write"); return 1; } return 0; } // invio il messaggio al client e poi il file 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); serror(MESY, fd_c, taglia, errmlog); goto _sendMF_cleanup; } if(!m) { m = MEFP; } if(writen(fd_c, m, strnlen(m, MAXLENMESS)+1) < 0) { perror("writen"); goto _sendMF_cleanup; } if(sendFile(f, fd_c, taglia) < 0) { perror("sendFile"); goto _sendMF_cleanup; } if(taglia_write(taglia, mlog) < 0) { perror("log write"); goto _sendMF_cleanup; } return; _sendMF_cleanup: return; } // invio il messaggio al client e poi n file void sendMessageFileN(char *m, fileT **f, int 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)+1) < 0) { perror("writen"); goto _sendMFN_cleanup; } for(int i=0; i> 1 & 0x1; // also >>1%2 fileT *removed = NULL; // file che è stato rimosso if(found && create) { // si vuole creare il file ma esiste già n += snprintf(tmp_buf+n, m-n, "Client %ld ha richiesto una openFile (flags = %x) sul file \"%s\" e' terminata con errore\n", fd_c, flags, filepath); errno = EEXIST; serror(MENT, fd_c, taglia, tmp_buf); return; } if(!found && !create) { // si vuole aprire e non creare un file inesistente 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); errno = ENOENT; serror(MENT, fd_c, taglia, tmp_buf); return; } if(found && !create) { 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\n", fd_c, flags, filepath); perror("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("dequeue"); serror(MESE, fd_c, taglia, tmp_buf); return; } fileT *f = createFileT(filepath, lock, fd_c, 1); // create new file 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("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("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); free(removed); return; } 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("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("enqueue"); serror(MESE, fd_c, taglia, tmp_buf); return; } // abbiamo aggiunto un file quindi il numero di file è cambiato // quindi bisogna fare un update del 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; } // codice non raggiungibile return; } void readFile(char *filepath, queueT *q, long fd_c, taglia_t *taglia) { // messaggio da scrivere sul 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\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\" e' terminata con errore\n", fd_c, filepath); errno = ENOENT; serror(MESE, fd_c, taglia, tmp_buf); return; } if(f->open != 0) { // file already open n += snprintf(tmp_buf+n, m-n, "Client %ld ha richiesto una readFile sul file \"%s\" e' terminata con errore\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\" e' terminata con successo\n", fd_c, filepath); 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){ // messaggio da scrivere sul 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; num = (num<=0 || num>getLen(q))? getLen(q) : num; fileT **toSend = calloc(num, sizeof(fileT *)); 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, we dont check if the client can actually modify // the files since we add them back immediatly for(int i=0;ifilepath); } sendMessageFileN(MEOK, toSend, num, 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) { // messaggio da scrivere sul 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; } // cerco il 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\n", fd_c, append, filepath, size); errno = ENOENT; serror(MENT, fd_c, taglia, tmp_buf); destroyFile(f); return; } // file non aperto || !append => locked || lock ma non si è proprietari 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\n", fd_c, append, filepath, size); errno = EPERM; serror(MENT, fd_c, taglia, tmp_buf); destroyFile(f); return; } int 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 fileT **removed = NULL; // array that may (worst case) hold all files to be sent to the client if(trueSizeAdded + getSize(q) > q->maxSize) { // writing would be more than capacity 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\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. Ha causato una capacity miss e ha fatto espellere i seguenti file:", fd_c, append, filepath, size); 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("calloc"); free(new); return -1; } strncpy(new->file, filepath, strnlen(filepath, MAXLENMESS-1)+1); new->fd = fd_c; new->next = NULL; // se la lista è vuota if(*waiting == NULL) { *waiting = new; return 0; } // se la lista ha almeno un elemento 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 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; } // destroy waiting structure void clearWaiting(waiting_t **waiting) { if(!waiting) { return; } waiting_t *curr = *waiting; waiting_t *next = NULL; while (curr) { next = curr->next; free(curr->file); free(curr); curr = next; } *waiting = NULL; return; }