Хуудсууд

2011-01-09

FUSE - хэрэглэгчийн орон зайн файл систем

Сүүлийн үед үүл, тархай систем гэсэн нэр томьёонуудыг олонтаа сонсох боллоо. Сүлжээ ашиглан зарим файлуудыг өөр тооцоолуур дээр хадгалаж, өөрийн тооцоолуур дээр байгаа мэтээр хандах гэх мэт олон дэвшилтэт арга замууд бие болсон ба ийм технологи дээр суурилсан олон start-up компани бий болсон.

Энэ бичлэгтээ ийм технологи хийхэд маш их тус болдог FUSE (хэрэглэгчийн орон зайн файл систем) дээр жишээ хийж үзсэнээ хуваалцья гэж бодлоо. Юниксжуу үйлдлийн систем дээр байгаа файл системийг хүссэнээрээ харж хэрэглэхэд тус болдог цөмийн модуль юм.

За тэгэхээр нэг ийм жишээ бичье. Та өөрийгөө тагнуулч байна гэж бод. Тагнаж байгаа хүнийхээ гэрт орлоо. Хэрэглэгчийн тодорхой хугацаанд хандсан файлыг эсвэл хамгийн сүүлд хандсан файлыг олохыг хүслээ гэж бодьё. Энэ жишээн дээр бичих програм маань тодорхой хавтасыг хувьсагч болгож өгөөд ажиллуулахад тэр хавтасан дотор байгаа файлуудыг хамгийн сүүлд өөрчлөгдсөн жил, сар, өдөр, цагаар нь ангилладаг програм байх юм.

Жишээ нь, доорх шиг хавтасыг ангилья гэж бодьё:

Дээрх зурган дээр файлын нэрүүдийн өмнөх багана файлын хамгийн сүүлд өөрчлөгдсөн огноо болохыг анхаарна уу!

Тэгвэл програмыг ажиллуулсаны хүчинд доорх байдлаар харж болох юм.

Дээрх зурган дээр бид ангилсан хавтасуудын тусламжтайгаар 2010 оны 05 сарын 10-нь 01 цагт хамгийн сүүлд өөрчлөгдсөн файлыг харж байгаа юм.

За ингээд жишээний мааны код:

modiffs.c


#define FUSE_USE_VERSION 26

static const char * modiffsVersion = "2010.12.24";

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <stdio.h>
#include <strings.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/xattr.h>
#include <dirent.h>
#include <unistd.h>
#include <fuse.h>
#include <time.h>

#define STACK_SIZE 250

typedef struct stack_s {
   char * items[STACK_SIZE];
   int size;
} stack;

int stack_exist(stack * s, char * elem) {
   int i = 0;
   for (i=0; i < s->size; i++) {
       if (strcmp(elem, s->items[i]) == 0) {
           return 1;
       }
   }
   return 0;
}

void stack_push(stack * s, char * elem) {
   s->items[s->size++] = elem;
}

int dir_depth(const char * path) {
   int depth = 0;
   char * temp = strchr(path, '/');
   while (temp != NULL) {
       depth++;
       temp = strchr(temp + 1, '/');
   }
   return depth;
}

char * full_path(char * upath, char * d_name) {
   char * ret = (char *) malloc(sizeof(char)*(strlen(upath) + strlen(d_name) + 1));
   strcpy(ret, upath);
   strcat(ret, d_name);
   return ret;
}

// Global to store our read-write path
char * rw_path;

// Translate an modiffs path into it's underlying filesystem path
static char * translate_path(const char * path) {

   char * rPath= malloc(sizeof(char)*(strlen(path)+strlen(rw_path)+1));

   strcpy(rPath, rw_path);
   if (rPath[strlen(rPath)-1]=='/') {
       rPath[strlen(rPath)-1]='\0';
   }
  
   int depth = dir_depth(path);
  
   if (strcmp("/", path) == 0) {
       strcat(rPath, "/");
   } else if (depth < 5) {
       strcat(rPath, "/");
   } else {
       int i = 0;
       char * temp = strchr(path, '/');
       while (i < (depth-1)) {
           i++;
           temp = strchr(temp + 1, '/');
       }
       strcat(rPath, "/");
       strcat(rPath, temp);
   }
  
   return rPath;
}

/*
* level:
* 0: year
* 1: month
* 2: day
* 3: hour
*/
char * last_attr(const char * path, int level) {   
   int depth = 0;
   char * cont = (char *) malloc(sizeof(char)*strlen(path));
   strcpy(cont, path);
   char * temp = strtok(cont, "/");
   while (depth < level && temp != NULL) {
       depth++;
       temp = strtok(NULL, "/");
   }
   return temp;
}

/******************************
*
* Callbacks for FUSE
*
******************************/

static int modiffs_getattr(const char * path, struct stat * stbuf) {
  
   int res = 0;
   int depth = dir_depth(path);
   memset(stbuf, 0, sizeof(struct stat));
  
   if (strcmp(path, "/") == 0) {
       stbuf->st_mode = S_IFDIR | 0755;
       stbuf->st_nlink = 1;
       stbuf->st_ctime = time(NULL);
       stbuf->st_atime = time(NULL);
       stbuf->st_mtime = time(NULL);
       stbuf->st_size = 4096;
   } else if (depth < 5) {       
       stbuf->st_mode = S_IFDIR | 0444;
       stbuf->st_nlink = 1;
       stbuf->st_ctime = time(NULL);
       stbuf->st_atime = time(NULL);
       stbuf->st_mtime = time(NULL);
       stbuf->st_size = 4096;
   } else {       
       char * upath = translate_path(path);
       res = lstat(upath, stbuf);
       free(upath);
       if(res == -1) {
           return -errno;
       }
   }

   return res;
}

static int modiffs_readlink(const char *path, char *buf, size_t size)
{
   int res;
   char * upath = translate_path(path);

   res = readlink(upath, buf, size - 1);
   free(upath);
   if(res == -1) {
       return -errno;
   }
   buf[res] = '\0';
   return 0;
}

static int modiffs_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi) {
  
   DIR * dp;
   struct dirent * de;
   int res;
  
   struct tm *ts;
  
   (void) offset;
   (void) fi;
  
   int depth;
  
   char * upath = translate_path(path);
  
   dp = opendir(upath);
  
   if(dp == NULL) {
       res = -errno;
       return res;
   }
  
   filler(buf, ".", NULL, 0);
   filler(buf, "..", NULL, 0);
  
   if (strcmp("/", path) == 0) {       
       stack years;
       years.size = 0;
       while((de = readdir(dp)) != NULL) {       
           struct stat st_modif;
           memset(&st_modif, 0, sizeof(st_modif));
           char * year = (char *) malloc(sizeof(char) * 5);           
           stat(full_path(upath, de->d_name), &st_modif);
           ts = localtime(&(st_modif.st_mtime));
           strftime(year, 255, "%Y", ts);
           if (stack_exist(&years, year) == 0) {
               stack_push(&years, year);
               filler(buf, year, NULL, 0);
           }
       }
   } else {
       depth = dir_depth(path);
       if (depth == 1) {
           stack months;
           months.size = 0;
           while((de = readdir(dp)) != NULL) {       
               struct stat st_modif;
               memset(&st_modif, 0, sizeof(st_modif));
               char * year = (char *) malloc(sizeof(char)*5);
               char * month = (char *) malloc(sizeof(char)*3);
               stat(full_path(upath, de->d_name), &st_modif);
               ts = localtime(&(st_modif.st_mtime));
               strftime(year, 255, "%Y", ts);
               strftime(month, 255, "%m", ts);
               if (strcmp(last_attr(path,0), year) == 0) {
                   if (stack_exist(&months, month) == 0) {
                       filler(buf, month, NULL, 0);
                       stack_push(&months, month);
                   }
               }
           }
       } else if (depth == 2) {
           stack days;
           days.size = 0;
           while((de = readdir(dp)) != NULL) {       
               struct stat st_modif;
               memset(&st_modif, 0, sizeof(st_modif));
               char * year = (char *) malloc(sizeof(char)*5);
               char * month = (char *) malloc(sizeof(char)*3);
               char * day = (char *) malloc(sizeof(char)*3);
               stat(full_path(upath, de->d_name), &st_modif);
               ts = localtime(&(st_modif.st_mtime));
               strftime(year, 255, "%Y", ts);
               strftime(month, 255, "%m", ts);
               strftime(day, 255, "%d", ts);
              
               if (strcmp(last_attr(path,0), year) == 0 &&
                   strcmp(last_attr(path,1), month) == 0) {
                   if (stack_exist(&days, day) == 0) {
                       stack_push(&days, day);
                       filler(buf, day, NULL, 0);
                   }
               }                           
           }
       } else if (depth == 3) {
           stack hours;
           hours.size = 0;
           while((de = readdir(dp)) != NULL) {       
               struct stat st_modif;
               memset(&st_modif, 0, sizeof(st_modif));
               char * year = (char *) malloc(sizeof(char)*5);
               char * month = (char *) malloc(sizeof(char)*3);
               char * day = (char *) malloc(sizeof(char)*3);
               char * hour = (char *) malloc(sizeof(char)*3);
               stat(full_path(upath, de->d_name), &st_modif);
               ts = localtime(&(st_modif.st_mtime));
               strftime(year, 255, "%Y", ts);
               strftime(month, 255, "%m", ts);
               strftime(day, 255, "%d", ts);
               strftime(hour, 255, "%H", ts);
              
               if (strcmp(last_attr(path,0), year) == 0 &&
                   strcmp(last_attr(path,1), month) == 0 &&
                   strcmp(last_attr(path,2), day) == 0) {
                   if (stack_exist(&hours, hour) == 0) {
                       stack_push(&hours, hour);
                       filler(buf, hour, NULL, 0);
                   }
               }                           
           }
       } else {
           while((de = readdir(dp)) != NULL) {
               if (strcmp(".", de->d_name) == 0 || strcmp("..", de->d_name) == 0) {
                   continue;
               }             
              
               struct stat st_modif;
               memset(&st_modif, 0, sizeof(st_modif));
               char * year = (char *) malloc(sizeof(char)*5);
               char * month = (char *) malloc(sizeof(char)*3);
               char * day = (char *) malloc(sizeof(char)*3);
               char * hour = (char *) malloc(sizeof(char)*3);
               stat(full_path(upath, de->d_name), &st_modif);
               ts = localtime(&(st_modif.st_mtime));
               strftime(year, 255, "%Y", ts);
               strftime(month, 255, "%m", ts);
               strftime(day, 255, "%d", ts);
               strftime(hour, 255, "%H", ts);
              
               if (strcmp(last_attr(path,0), year) == 0 &&
                   strcmp(last_attr(path,1), month) == 0 &&
                   strcmp(last_attr(path,2), day) == 0 &&
                   strcmp(last_attr(path,3), hour) == 0) {
                  
                   struct stat st;
                   memset(&st, 0, sizeof(st));
                   st.st_ino = de->d_ino;
                   st.st_mode = de->d_type << 12;

                   if (filler(buf, de->d_name, &st, 0)) {
                       break;
                   }
               }
           }
       }
   }
  
   free(upath);
   closedir(dp);
  
   return 0;
}

static int modiffs_mknod(const char *path, mode_t mode, dev_t rdev)
{
   (void)path;
   (void)mode;
   (void)rdev;
   return -EROFS;
}

static int modiffs_mkdir(const char *path, mode_t mode)
{
   (void)path;
   (void)mode;
   return -EROFS;
}

static int modiffs_unlink(const char *path)
{
   (void)path;
   return -EROFS;
}

static int modiffs_rmdir(const char *path)
{
   (void)path;
   return -EROFS;
}

static int modiffs_symlink(const char *from, const char *to)
{
   (void)from;
   (void)to;
   return -EROFS;
}

static int modiffs_rename(const char *from, const char *to)
{
   (void)from;
   (void)to;
   return -EROFS;
}

static int modiffs_link(const char *from, const char *to)
{
   (void)from;
   (void)to;
   return -EROFS;
}

static int modiffs_chmod(const char *path, mode_t mode)
{
   (void)path;
   (void)mode;
   return -EROFS;

}

static int modiffs_chown(const char *path, uid_t uid, gid_t gid)
{
   (void)path;
   (void)uid;
   (void)gid;
   return -EROFS;
}

static int modiffs_truncate(const char *path, off_t size)
{
   (void)path;
   (void)size;
   return -EROFS;
}

static int modiffs_utime(const char *path, struct utimbuf *buf)
{
   (void)path;
   (void)buf;
   return -EROFS;
}

static int modiffs_open(const char *path, struct fuse_file_info *finfo)
{
   int res;
  
   int flags = finfo->flags;

   if ((flags & O_WRONLY) || (flags & O_RDWR) || (flags & O_CREAT) || (flags & O_EXCL) || (flags & O_TRUNC) || (flags & O_APPEND)) {
       return -EROFS;
   }
  
   char * upath = translate_path(path);

   res = open(upath, flags);

   free(upath);
   if(res == -1) {
       return -errno;
   }
   close(res);
  
   return 0;
}

static int modiffs_read(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *finfo)
{
   int fd;
   int res;
   (void)finfo;

   char * upath = translate_path(path);
   fd = open(upath, O_RDONLY);
   free(upath);
   if(fd == -1) {
       res = -errno;
       return res;
   }
   res = pread(fd, buf, size, offset);

   if(res == -1) {
       res = -errno;
   }
   close(fd);
   return 0;
  
}

static int modiffs_write(const char *path, const char *buf, size_t size, off_t offset, struct fuse_file_info *finfo)
{
   (void)path;
   (void)buf;
   (void)size;
   (void)offset;
   (void)finfo;
   return -EROFS;
}

static int modiffs_statfs(const char *path, struct statvfs *st_buf)
{
   int res;
   char * upath = translate_path(path);
   res = statvfs(upath, st_buf);
   free(upath);
   if (res == -1) {
       return -errno;
   }
   return 0;
}

static int modiffs_release(const char *path, struct fuse_file_info *finfo)
{
   (void) path;
   (void) finfo;
   return 0;
}

static int modiffs_fsync(const char *path, int crap, struct fuse_file_info *finfo)
{
   (void) path;
   (void) crap;
   (void) finfo;
   return 0;
}

static int modiffs_access(const char *path, int mode)
{
   int res;
   char *upath = translate_path(path);

   /* Don't pretend that we allow writing
    * Chris AtLee <chris@atlee.ca>
    */
   if (mode & W_OK)
       return -EROFS;

   res = access(upath, mode);
   free(upath);
   if (res == -1) {
       return -errno;
   }
   return res;
}

struct fuse_operations modiffs_oper = {
   .getattr     = modiffs_getattr,
   .readlink    = modiffs_readlink,
   .readdir     = modiffs_readdir,
   .mknod       = modiffs_mknod,
   .mkdir       = modiffs_mkdir,
   .symlink     = modiffs_symlink,
   .unlink      = modiffs_unlink,
   .rmdir       = modiffs_rmdir,
   .rename      = modiffs_rename,
   .link        = modiffs_link,
   .chmod       = modiffs_chmod,
   .chown       = modiffs_chown,
   .truncate    = modiffs_truncate,
   .utime       = modiffs_utime,
   .open        = modiffs_open,
   .read        = modiffs_read,
   .write       = modiffs_write,
   .statfs      = modiffs_statfs,
   .release     = modiffs_release,
   .fsync       = modiffs_fsync,
   .access      = modiffs_access
};
enum {
   KEY_HELP,
   KEY_VERSION,
};

static void usage(const char* progname)
{
   fprintf(stdout,
           "usage: %s readwritepath mountpoint [options]\n"
           "\n"
           "   Mounts readwritepath as a read-only mount at mountpoint\n"
           "\n"
           "general options:\n"
           "   -o opt,[opt...]     mount options\n"
           "   -h  --help          print help\n"
           "   -V  --version       print version\n"
           "\n", progname);
}

static int modiffs_parse_opt(void *data, const char *arg, int key,
                         struct fuse_args *outargs)
{
   (void) data;

   switch (key)
   {
   case FUSE_OPT_KEY_NONOPT:
       if (rw_path == 0)
       {
           rw_path = strdup(arg);
           return 0;
       }
       else
       {
           return 1;
       }
   case FUSE_OPT_KEY_OPT:
       return 1;
   case KEY_HELP:
       usage(outargs->argv[0]);
       exit(0);
   case KEY_VERSION:
       fprintf(stdout, "ROFS version %s\n", modiffsVersion);
       exit(0);
   default:
       fprintf(stderr, "see `%s -h' for usage\n", outargs->argv[0]);
       exit(1);
   }
   return 1;
}

static struct fuse_opt modiffs_opts[] = {
   FUSE_OPT_KEY("-h",          KEY_HELP),
   FUSE_OPT_KEY("--help",      KEY_HELP),
   FUSE_OPT_KEY("-V",          KEY_VERSION),
   FUSE_OPT_KEY("--version",   KEY_VERSION),
   FUSE_OPT_END
};

int main(int argc, char *argv[])
{
   struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
   int res;

   res = fuse_opt_parse(&args, &rw_path, modiffs_opts, modiffs_parse_opt);
   if (res != 0)
   {
       fprintf(stderr, "Invalid arguments\n");
       fprintf(stderr, "see `%s -h' for usage\n", argv[0]);
       exit(1);
   }
   if (rw_path == 0)
   {
       fprintf(stderr, "Missing readwritepath\n");
       fprintf(stderr, "see `%s -h' for usage\n", argv[0]);
       exit(1);
   }

   fuse_main(args.argc, args.argv, &modiffs_oper, NULL);

   return 0;
}

Дээрх кодыг Убунту дээр ирдэг fuse-rofs багцын эх код дээр өөрчлөлт хийж бий болгосоныг анхаарна уу!

Эмхэтгэж ажиллуулахын тулд:

test.sh

gcc -o modiffs -Wall -ansi -W -std=c99 -g -ggdb -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -lfuse modiffs.c fusermount -u /home/dagvadorj/Desktop/u sudo umount /home/dagvadorj/Desktop/u ./modiffs /home/dagvadorj/Desktop/untitled /home/dagvadorj/Desktop/u

No comments:

Post a Comment