[AVFS] patch for magic char inside filenames

Ralf Hoffmann <[email protected]> Mon, 07 Mar 2005 22:55:16 +0100 (CET)
Newsgroups gmane.comp.file-systems.avfs.user
Message-ID <[email protected]>
Hi,

I attached my new patch for supporting the magic char in filenames. The
code tries to find for each path segment the longest part which is a
existing file (no matter if real local or inside an archive). This way
even "test.tar.gz#utar#ugz#utar" works for a real file
"test.tar.gz#utar". The magic char is also allowed in virtual files.
The code works well for me but I only tested it with the shared
library. I don't know if there are problems with the code or preload
method.

In one of my first mails I mentioned the problem about the inode number
of /#avfsstat/cache which is zero. Inode numbers 0 and 1 are reserved
so it should be at least 2.
The inode is calculated in src/state.c:297

      buf->ino = (int) stf + st_paramhash(sf->stent->param);

But for /#avfsstat stf and the hash is zero so the inode is also zero.
Would it be okay to just add 2 to guarantee the minimum inode number?

And another question about archive creation: The readme writes a few
words about it but creating a directory test.tar.gz#+ just creates the
directory and no archive (with my patch, without my patch the makedir
fails). Does this not work with the shared library?

Anyway, I'm looking forward for your comments.

Best Regards,

Ralf Hoffmann

-- 
Homepage: http://www.boomerangsworld.de
E-Mail: Ralf Hoffmann <[email protected]>
  english or german
avfs-cvs-patch2.diff (application/octet-stream, 11.6 KB)
diff -purN avfs-cvs/include/virtual.h avfs-cvs-my/include/virtual.h
--- avfs-cvs/include/virtual.h	2004-01-05 13:46:49.000000000 +0100
+++ avfs-cvs-my/include/virtual.h	2005-02-06 13:23:25.000000000 +0100
@@ -52,4 +52,7 @@ int            virt_closedir  (DIR *dirp
 struct dirent *virt_readdir   (DIR *dirp);
 void           virt_rewinddir (DIR *dirp);
 
+int            virt_remove    (const char *path);
+int            virt_islocal   (const char *path);
+
 #endif /* _VIRTUAL_H */
diff -purN avfs-cvs/src/fdops.c avfs-cvs-my/src/fdops.c
--- avfs-cvs/src/fdops.c	2001-11-05 13:31:29.000000000 +0100
+++ avfs-cvs-my/src/fdops.c	2005-02-14 00:17:18.000000000 +0100
@@ -195,27 +195,6 @@ avoff_t av_fd_lseek(int fd, avoff_t offs
     return res;
 }
 
-static void avdirent_escape_magic(struct avdirent *buf)
-{
-    char *newname;
-    char *src;
-    char *dst;
-
-    if(strchr(buf->name, AVFS_SEP_CHAR) == NULL)
-        return;
-
-    newname = av_malloc(strlen(buf->name) * 2 + 1);
-    dst = newname;
-    for(src = buf->name; *src != '\0'; src++) {
-        *dst++ = *src;
-        if(*src == AVFS_SEP_CHAR)
-            *dst++ = AVFS_SEP_CHAR;
-    }
-    *dst = '\0';
-    av_free(buf->name);
-    buf->name = newname;
-}
-
 int av_fd_readdir(int fd, struct avdirent *buf, avoff_t *posp)
 {
     int res;
@@ -230,8 +209,6 @@ int av_fd_readdir(int fd, struct avdiren
 	res = avfs->readdir(vf, buf);
         AVFS_UNLOCK(avfs);
 
-        if(res > 0)
-            avdirent_escape_magic(buf);
 
 	put_file(vf);
     }
diff -purN avfs-cvs/src/parse.c avfs-cvs-my/src/parse.c
--- avfs-cvs/src/parse.c	2001-05-17 18:16:03.000000000 +0200
+++ avfs-cvs-my/src/parse.c	2005-03-04 13:02:15.000000000 +0100
@@ -12,6 +12,7 @@
 #include "version.h"
 #include "local.h"
 #include "mod_static.h"
+#include "operutil.h"
 
 #include <stdio.h>
 #include <ctype.h>
@@ -39,6 +40,7 @@ struct parse_state {
     int resolvelast;
     int nextseg;
     int linkctr;
+    int first_seg;  /* true if no segment was analysed, see segment_len() comment */
 };
 
 static int copyrightstat_get(struct entry *ent, const char *param, char **retp)
@@ -213,6 +215,28 @@ void av_add_avfs(struct avfs *newavfs)
     AV_UNLOCK(avfs_lock);
 }
 
+static int av_copy_parsestate(struct parse_state *ps, struct parse_state *destps)
+{
+    destps->linkctr = ps->linkctr;
+    destps->nextseg = ps->nextseg;
+    destps->resolvelast = ps->resolvelast;
+    destps->islink = ps->islink;
+
+    if(ps->path)
+      destps->path = av_strdup(ps->path);
+    else
+      destps->path = NULL;
+
+    destps->first_seg = ps->first_seg;
+
+    if(ps->prevseg)
+      destps->prevseg = av_strdup(ps->prevseg);
+    else
+      destps->prevseg = NULL;
+
+    av_copy_ventry(ps->ve, &(destps->ve));
+    return 0;
+}
 
 static void set_prevseg(struct parse_state *ps, const char *name)
 {
@@ -475,36 +499,30 @@ static int lookup_avfs(struct parse_stat
     return res;
 }
 
-static void preprocess_name(char *name)
-{
-    char *s, *d;
-
-    for(s = name, d = name; *s; s++, d++) {
-        if(s[0] == AVFS_SEP_CHAR && s[1] == AVFS_SEP_CHAR)
-            s++;
-	*d = *s;
-    }
-    *d = '\0';
-}
-
-static int lookup_segment(struct parse_state *ps)
+static int lookup_segment(struct parse_state *ps, int noavfs)
 {
     int res;
     char *name = ps->path;
     ventry *ve = ps->ve;
 
-    if(name[0] == AVFS_SEP_CHAR && name[1] != AVFS_SEP_CHAR)
-        res = lookup_avfs(ps, name+1);
+    /* only enter next avfs hierarchie if the magic char is not the
+       very first char (first_seg) and we really want this action
+       (noavfs needed by segment_islocal() test */
+
+    if((name[0] == AVFS_SEP_CHAR) && (noavfs == 0) && (ps->first_seg == 0))
+      res = lookup_avfs(ps, name+1);
     else {
+        /* reset first_seg as we now process a path segment and from now on 
+	   the next magic char is always the magic char and not from a local
+	   filename */
+        ps->first_seg = 0;
+
         for(;*name && *name == AV_DIR_SEP_CHAR; name++);
         set_prevseg(ps, name);
 
 	if((ve->mnt->avfs->flags & AVF_NEEDSLASH) != 0)
 	   name = ps->path;
         
-        if(ve->mnt->base != NULL)
-            preprocess_name(name);
-        
         if(name[0] != '\0')
             res = lookup_virtual(ps, name);
         else
@@ -514,28 +532,98 @@ static int lookup_segment(struct parse_s
     return res;
 }
 
-static unsigned int segment_len(struct parse_state *ps)
+static int segment_islocal(struct parse_state *ps, unsigned int seglen )
 {
-    const char *s = ps->path;
-    unsigned int seglen;
+    int islocal = 0;
+    char c;
+    struct parse_state tempps;
+    int f;
 
-    if(s[0] == AVFS_SEP_CHAR && s[1] != AVFS_SEP_CHAR)
+    /* we will copy the whole parse_state and try to find a local entry
+       if open succeed there is a local file and we can return true */
+
+    av_copy_parsestate( ps, &tempps );
+    
+    tempps.nextseg = seglen;
+    c = tempps.path[seglen];
+    tempps.path[seglen] = '\0';
+    
+    /* we force lookup_segment() to not enter any avfs hierarchie */
+    lookup_segment(&tempps, 1);
+    f = av_fd_open_entry(tempps.ve, AVO_RDONLY, 0);
+    av_free_ventry(tempps.ve);
+    av_free(tempps.path);
+    av_free(tempps.prevseg);
+    if ( f >= 0 ) {
+      islocal = 1;
+      av_fd_close(f);
+    }
+    return islocal;
+}
+
+static unsigned int segment_len(struct parse_state *ps, int ignoreMagic)
+{
+    const char *s = ps->path,
+               *first_s = ps->path;
+    unsigned int seglen, orig_seglen;
+    int found_magic = 0, search_avfs_key = 0;
+
+    /* this function will find the next segment len by also checking for local files
+       with magic chars inside the filename
+       two cases:
+       1.magic at the beginning:
+         in this case we are searching for an avfs key (#utar, #ugz...)
+	 so we will stop at the next magic char or dir separator
+	 except when this magic char is the very first character
+	 where it makes no sense to accept this as an avfs key
+	 (imagine open("#utar"))
+	 For this case there is a new flag first_seg which takes
+	 care of this
+       2.Otherwise we are searching for the longest path segment
+         starting at the next dir separator skipping any magic char
+	 we stop after first local hit
+	 If no local file was found we return whole segment len to be able
+	 to open new files with magic chars
+       If no magic char was found or we are forced to ignore it
+       we immediately return the whole path segment without any checking
+    */
+
+    if(s[0] == AVFS_SEP_CHAR) {
         s++;
-    else while(*s == AV_DIR_SEP_CHAR)
+	if(ps->first_seg == 0)
+            search_avfs_key = 1;
+    } else while(*s == AV_DIR_SEP_CHAR)
         s++;
     
     while(*s && *s != AV_DIR_SEP_CHAR) {
-        if(s[0] == AVFS_SEP_CHAR) {
-            if(s[1] == AVFS_SEP_CHAR)
-                s++;
-            else
-                break;
+        if(*s == AVFS_SEP_CHAR) {
+            if(found_magic == 0) {
+	        first_s = s;
+	    }
+	    found_magic++;
         }
         s++;
     }
     seglen = s - ps->path;
 
-    return seglen;
+    if((ignoreMagic == 1) || (found_magic == 0)) return seglen;
+
+    /* a magic char was already found so first_s is correct */
+    if(search_avfs_key == 1)
+        return (first_s - ps->path);
+
+    orig_seglen = seglen;
+    /* found magic char so check for existing local file */
+    while(seglen > 0) {
+        if(segment_islocal(ps, seglen) == 1)
+	    break;
+        for(seglen = seglen - 1;
+            (seglen > 0) && ( ps->path[seglen] != AVFS_SEP_CHAR );
+            seglen--);
+    }
+
+    if(seglen > 0) return seglen;
+    else return orig_seglen;
 }
 
 static int is_last(struct parse_state *ps, unsigned int seglen)
@@ -561,7 +649,7 @@ static struct avfs *get_local_avfs()
     return localavfs;
 }
 
-static int parse_path(struct parse_state *ps);
+static int parse_path(struct parse_state *ps, int force_localfile);
 
 static int follow_link(struct parse_state *ps)
 {
@@ -588,7 +676,7 @@ static int follow_link(struct parse_stat
         
         res = lookup_virtual(&linkps, NULL);
         if(res == 0)
-            res = parse_path(&linkps);
+            res = parse_path(&linkps, 0);
     }
     else {
         av_free_ventry(ps->ve);
@@ -597,8 +685,9 @@ static int follow_link(struct parse_stat
         linkps.ve->mnt = new_mount(NULL, get_local_avfs(), NULL);
         linkps.ve->data = av_strdup("");
 
-        res = parse_path(&linkps);
+        res = parse_path(&linkps, 0);
     }
+
     
     av_free(buf);
     ps->ve = linkps.ve;
@@ -606,26 +695,27 @@ static int follow_link(struct parse_stat
     return res;
 }
 
-static int parse_path(struct parse_state *ps)
+static int parse_path(struct parse_state *ps, int force_localfile)
 {
     int res = 0;
     int numseg = 0;
 
     ps->prevseg = av_strdup("");
-
+    ps->first_seg = 1;
     while(ps->path[0]) {
         unsigned int seglen;
         int lastseg;
         char c;
 
-        seglen = segment_len(ps);
+	seglen = segment_len(ps, force_localfile);
+	
         lastseg = is_last(ps, seglen);
         ps->nextseg = seglen;
         c = ps->path[seglen];
         ps->path[seglen] = '\0';
         ps->islink = 0;
         
-        res = lookup_segment(ps);
+        res = lookup_segment(ps,0);
         if(res < 0)
             break;
         
@@ -650,7 +740,6 @@ static int parse_path(struct parse_state
     return res;
 }
 
-
 int av_get_ventry(const char *path, int resolvelast, ventry **resp)
 {
     int res;
@@ -673,7 +762,23 @@ int av_get_ventry(const char *path, int 
     ps.ve->mnt = new_mount(NULL, get_local_avfs(), NULL);
     ps.ve->data = av_strdup("");
 
-    res = parse_path(&ps);
+    res = parse_path(&ps, 0);
+
+    /* no ventry so force localfile to be able to create files with
+       the magic character inside filename */
+    if(res < 0) {
+        av_free(copypath);
+        copypath = av_strdup(path);
+        av_free_ventry(ps.ve);
+        ps.path = copypath;
+        ps.resolvelast = resolvelast;
+        ps.linkctr = 10;
+        AV_NEW(ps.ve);
+        ps.ve->mnt = new_mount(NULL, get_local_avfs(), NULL);
+        ps.ve->data = av_strdup("");
+        res = parse_path(&ps, 1);
+    }
+
     if(res < 0) {
         av_free_ventry(ps.ve);
         *resp = NULL;
@@ -768,8 +873,7 @@ static int ipath_len(const char *s)
 {
     int cnt;
 
-    for(cnt = 0; *s; s++, cnt++) 
-        if(*s == AVFS_SEP_CHAR) cnt++;
+    for(cnt = 0; *s; s++, cnt++);
 
     return cnt;
 }
@@ -778,8 +882,6 @@ static void ipath_copy(char *dst, const 
 {
     for(; *src; dst++, src++) {
         *dst = *src;
-        if(*src == AVFS_SEP_CHAR)
-            *++dst = AVFS_SEP_CHAR;
     }
     *dst = '\0';
 }
diff -purN avfs-cvs/src/virtual.c avfs-cvs-my/src/virtual.c
--- avfs-cvs/src/virtual.c	2004-01-05 13:46:49.000000000 +0100
+++ avfs-cvs-my/src/virtual.c	2005-02-10 20:45:59.000000000 +0100
@@ -671,3 +671,45 @@ int virt_link(const char *path, const ch
     errno = errno_save;
     return 0;
 }
+
+int virt_remove(const char *path)
+{
+    struct stat stbuf;
+
+    if(path != NULL) {
+        if(virt_lstat(path, &stbuf) == 0) {
+            if(S_ISDIR(stbuf.st_mode)) {
+                return virt_rmdir(path);
+            } else {
+                return virt_unlink(path);
+            }
+        }
+    }
+
+    errno = EFAULT;
+    return -1;
+}
+
+int virt_islocal(const char *path)
+{
+    int res;
+    ventry *ve;
+    int errno_save = errno;
+    int erg = 0;
+
+    res = av_get_ventry(path, 0, &ve);
+    if(res == 0) {
+        if(ve->mnt->base == NULL)
+            erg = 1;
+        else
+            erg = 0;
+        av_free_ventry(ve);
+    }
+    if(res < 0) {
+        errno = -res;
+        return -1;
+    }
+
+    errno = errno_save;
+    return erg;
+}