Wednesday | 28 FEB 2024
[ previous ]
[ next ]

FUSE Tutorial - 09 Full Code So Far

Title:
Date: 2023-02-06
Tags:  

The command to compile and run the application:

gcc -lsqlite3 -lfuse -D_FILE_OFFSET_BITS=64 sfs.c -o sfs
./sfs -f db.sqlite dir

The full code as of now:

#define FUSE_USE_VERSION 29
#include <fuse.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sqlite3.h>

int count_slashes(const char *str) {
    int count = 0;
    int i;
    for (i = 0; i < strlen(str); i++) {
        if (str[i] == '/') {
            count++;
        }
    }
    return count;
}

int sfs_getattr(const char *path, struct stat *st)
{
    printf("Getting attributes for: %s\n",path);

    int slashes = count_slashes(path);

    if (slashes == 1) {
        st->st_mode = S_IFDIR | 0755;
        st->st_nlink = 2;
        st->st_size = 4096;           // file size

    } else {
        st->st_mode = S_IFREG | 0644;
        st->st_nlink = 2;
        st->st_size = 4096;
    }

    st->st_uid = getuid();
    st->st_gid = getgid();

    return 0;
}

int sq_getTables(void *buffer, fuse_fill_dir_t filler) {
    sqlite3 *db;
    int rc = sqlite3_open("db.sqlite", &db);

    char *sql = "select name from sqlite_master where type = 'table' and name not like 'sqlite_%';";

    sqlite3_stmt *stmt = NULL;
    sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);

    rc = sqlite3_step(stmt);

    while (rc != SQLITE_DONE && rc != SQLITE_OK) {
        const unsigned char * table = sqlite3_column_text(stmt,0);
        filler(buffer, table, NULL, 0);
        rc = sqlite3_step(stmt);
    }

    rc = sqlite3_finalize(stmt);
    rc = sqlite3_close(db);

    return rc;
}

int sq_getKeys(char *tableName, void *buffer, fuse_fill_dir_t filler) {
    sqlite3 *db;
    int rc = sqlite3_open("db.sqlite", &db);

    char *sql = "select * from ";
    char *sc = ";";

    int length = strlen(sql) + strlen(tableName) + strlen(sc);
    char *result = malloc(length + 1);

    strcpy(result, sql);
    strcat(result, tableName);
    strcat(result, sc);

    sqlite3_stmt *stmt = NULL;
    sqlite3_prepare_v2(db, result, -1, &stmt, NULL);

    rc = sqlite3_step(stmt);

    while (rc != SQLITE_DONE && rc != SQLITE_OK) {
        const unsigned char *key = sqlite3_column_text(stmt,0);
        filler(buffer, key, NULL, 0);
        rc = sqlite3_step(stmt);
    }

    rc = sqlite3_finalize(stmt);
    rc = sqlite3_close(db);

    free(result);

    return rc;
}

int sfs_readdir(const char *path, void *buffer, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi)
{
    printf("Calling readdir on: %s\n",path);
    int slashes = count_slashes(path);

    filler(buffer, ".", NULL, 0);
    filler(buffer, "..", NULL, 0);

    if (slashes == 1) {
        if (!strcmp(path, "/")) {
            sq_getTables(buffer, filler);

        } else {
            int length = strlen(path);
            char *new_path = malloc(length + 1);
            strcpy(new_path, path);

            char *tableName = strtok(new_path, "/");
            sq_getKeys(tableName, buffer, filler);

            free(new_path);
        }
    }
    return 0;
}

char *sq_getData(char *tableName, char *key, char *buffer, off_t offset) {
    sqlite3 *db;
    int rc = sqlite3_open("db.sqlite", &db);

    char *sql = "select * from ";
    char *where = " where title = '";
    char *sc = "';";

    int length = strlen(sql) + strlen(tableName) + strlen(where) + strlen(key) + strlen(sc);
    char *query = malloc(length + 1);

    strcpy(query, sql);
    strcat(query, tableName);
    strcat(query, where);
    strcat(query, key);
    strcat(query, sc);

    sqlite3_stmt *stmt = NULL;
    sqlite3_prepare_v2(db, query, -1, &stmt, NULL);

    rc = sqlite3_step(stmt);

    int count = sqlite3_column_count(stmt);
    char **strings = (char**) malloc(count * sizeof(char*));

    int i;
    for (i = 0; i<count; i++) {
        const char * col = sqlite3_column_name(stmt, i);
        const unsigned char *val = sqlite3_column_text(stmt,i);

        int length = snprintf(NULL, 0, "%s = '%s'", col, val);
        strings[i] = (char*) malloc(length + 1);
        snprintf(strings[i], length + 1, "%s = '%s'", col,val);
    }

    rc = sqlite3_finalize(stmt);
    rc = sqlite3_close(db);

    free(query);

    int total_length = 0;
    for (i = 0; i < count; i++) {
        total_length += strlen(strings[i]);
    }

    char *result = (char*) malloc(total_length + count - 1);
    result[0] = '\0';
    for (i = 0; i < count; i++) {
        strcat(result, strings[i]);
        if (i < count - 1) {
            strcat(result, "\n");
        }
    }

    for (i = 0; i < count; i++) {
        free(strings[i]);
    }
    free(strings);

    return result;
}

static int sfs_read(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi) {
    printf("Reading: %s\n",path);

    int slashes = count_slashes(path);
    if (slashes != 2) {
        return 0;
    }

    char *new_path = malloc(strlen(path) + 1);
    strcpy(new_path, path);

    char *tableName = strtok(new_path, "/");
    char *key = strtok(NULL, "/");

    char* filecontent = sq_getData(tableName, key, buf, offset);

    size_t len = strlen(filecontent);
    if (offset >= len) {
        return 0;
    }

    if (offset + size > len) {
        memcpy(buf, filecontent + offset, len - offset);
        return len - offset;
    }

    memcpy(buf, filecontent + offset, size);

    free(new_path);
    return size;
}

int sq_updateData(char *tableName, char *key, const char *buffer) {
    sqlite3 *db;
    int rc = sqlite3_open("db.sqlite", &db);

    char *data = strdup(buffer);
    int datalen = strlen(data);

    int i;
    for (i = 0; i<datalen; i++) {
        if (data[i] == '\n') {
            data[i] = ',';
        }
    }

    if (data[datalen-1] == ',') {
        data[datalen-1] = '\0'; 
    }

    int length = snprintf(NULL, 0, "update %s set %s where title = '%s';", tableName, data, key);
    char *query = (char*) malloc(length + 1);
    snprintf(query, length, "update %s set %s where title = '%s';", tableName, data, key);

    sqlite3_stmt *stmt = NULL;
    sqlite3_prepare_v2(db, query, -1, &stmt, NULL);
    rc = sqlite3_step(stmt);

    rc = sqlite3_finalize(stmt);
    rc = sqlite3_close(db);

    free(data);

    return rc;
}

static int sfs_write(const char *path, const char *buf, size_t size, off_t offset, struct fuse_file_info *fi) {
    printf("Writing: %s\n",path);

    int slashes = count_slashes(path);
    if (slashes != 2) {
        return 0;
    }

    char *new_path = malloc(strlen(path) + 1);
    strcpy(new_path, path);

    char *tableName = strtok(new_path, "/");
    char *key = strtok(NULL, "/");

    sq_updateData(tableName, key, buf);

    return strlen(buf);
}

static struct fuse_operations sfs_ops = {
    .getattr = sfs_getattr,
    .readdir = sfs_readdir,
    .read = sfs_read,
    .write = sfs_write,
};

int main(int argc, char **argv)
{
    int i;

    for (i = 1; i < argc && argv[i][0] == '-'; i++);
    if (i < argc) {
        memcpy(&argv[i], &argv[i+1], (argc-i) * sizeof(argv[0]));
        argc--;
    }

    printf("Lighting FUSE...\n");
    return fuse_main(argc, argv, &sfs_ops, NULL);
}