SQLite Archiver
Check-in [4a0ed63dae]
Not logged in

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Add symlink support to sqlar.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1:4a0ed63daef97c4b3380aabc6578e3056fe54bca
User & Date: dan 2017-12-02 19:15:43
References
2017-12-14
06:03 New ticket [9dafc30484] sqlar tosses file mod times. artifact: 864e9759e5 user: anonymous
Context
2017-12-04
16:24
Update README.md to describe how symbolic links are stored in an archive. check-in: ef2844a7e0 user: dan tags: trunk
2017-12-02
19:15
Add symlink support to sqlar. check-in: 4a0ed63dae user: dan tags: trunk
2017-05-12
15:32
Update the built-in SQLite to the first 3.19.0 beta. check-in: 84cb978770 user: drh tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to sqlar.c.

    42     42        "   -x      Extract files from archive\n"
    43     43        "   -v      Verbose output\n"
    44     44     );
    45     45     exit(1);
    46     46   }
    47     47   
    48     48   /*
    49         -** The database schema:
           49  +** The database schema. Each file, directory or symlink in an archive
           50  +** is represented by a single row in this table. 
           51  +**
           52  +** name:  Path to file-system entry (text).
           53  +** mode:  Value of stat.st_mode returned by stat() call (integer).
           54  +** mtime: Value of stat.st_mtime returned by stat() call (integer).
           55  +** sz:    Size of file in bytes. Always 0 for a directory. -1 for symlink.
           56  +** data:  Blob containing file contents. NULL for a directory. Text value
           57  +**        containing linked path for a symlink.
    50     58   */
    51     59   static const char zSchema[] =
    52     60     "CREATE TABLE IF NOT EXISTS sqlar(\n"
    53     61     "  name TEXT PRIMARY KEY,\n"
    54     62     "  mode INT,\n"
    55     63     "  mtime INT,\n"
    56     64     "  sz INT,\n"
................................................................................
   405    413     FILE *out;
   406    414     make_parent_directory(zFilename);
   407    415     if( pCompr==0 ){
   408    416       rc = mkdir(zFilename, iMode);
   409    417       if( rc ) errorMsg("cannot make directory: %s\n", zFilename);
   410    418       return;
   411    419     }
   412         -  out = fopen(zFilename, "wb");
   413         -  if( out==0 ) errorMsg("cannot open for writing: %s\n", zFilename);
   414         -  if( sz==nCompr ){
   415         -    if( sz>0 && fwrite(pCompr, sz, 1, out)!=1 ){
   416         -      errorMsg("failed to write: %s\n", zFilename);
          420  +  if( sz<0 ){
          421  +    /* This is a symlink. */
          422  +    if( symlink(pCompr, zFilename)<0 ){
          423  +      errorMsg("failed to create symlink: %s -> %s\n", zFilename, pCompr);
   417    424       }
   418    425     }else{
   419         -    pOut = sqlite3_malloc( sz+1 );
   420         -    if( pOut==0 ) errorMsg("cannot allocate %d bytes\n", sz+1);
   421         -    nOut = sz;
   422         -    rc = uncompress((Bytef*)pOut, &nOut, (const Bytef*)pCompr, nCompr);
   423         -    if( rc!=Z_OK ) errorMsg("uncompress failed for %s\n", zFilename);
   424         -    if( nOut>0 && fwrite(pOut, nOut, 1, out)!=1 ){
   425         -      errorMsg("failed to write: %s\n", zFilename);
          426  +    out = fopen(zFilename, "wb");
          427  +    if( out==0 ) errorMsg("cannot open for writing: %s\n", zFilename);
          428  +    if( sz==nCompr ){
          429  +      if( sz>0 && fwrite(pCompr, sz, 1, out)!=1 ){
          430  +        errorMsg("failed to write: %s\n", zFilename);
          431  +      }
          432  +    }else{
          433  +      pOut = sqlite3_malloc( sz+1 );
          434  +      if( pOut==0 ) errorMsg("cannot allocate %d bytes\n", sz+1);
          435  +      nOut = sz;
          436  +      rc = uncompress((Bytef*)pOut, &nOut, (const Bytef*)pCompr, nCompr);
          437  +      if( rc!=Z_OK ) errorMsg("uncompress failed for %s\n", zFilename);
          438  +      if( nOut>0 && fwrite(pOut, nOut, 1, out)!=1 ){
          439  +        errorMsg("failed to write: %s\n", zFilename);
          440  +      }
          441  +      sqlite3_free(pOut);
   426    442       }
   427         -    sqlite3_free(pOut);
          443  +    fclose(out);
          444  +
          445  +    rc = chmod(zFilename, iMode&0777);
          446  +    if( rc ) errorMsg("cannot change mode to %03o: %s\n", iMode&0777,zFilename);
   428    447     }
   429         -  fclose(out);
   430         -  rc = chmod(zFilename, iMode&0777);
   431         -  if( rc ) errorMsg("cannot change mode to %03o: %s\n", iMode, zFilename);
   432    448   }
   433    449   
   434    450   /*
   435    451   ** Error out if there are any issues with the given filename
   436    452   */
   437    453   static void check_filename(const char *z){
   438    454     if( strncmp(z, "../", 3)==0 || sqlite3_strglob("*/../*", z)==0 ){
   439    455       errorMsg("Filename with '..' in its path: %s\n", z);
   440    456     }
   441    457     if( sqlite3_strglob("*\\*", z)==0 ){
   442    458       errorMsg("Filename with '\\' in its name: %s\n", z);
   443    459     }
   444    460   }
          461  +
          462  +/*
          463  +** File zSymlink is a symbolic link. Figure out the path that the link 
          464  +** points to and bind it (as text) to parameter iVar of statement pStmt.
          465  +*/
          466  +static void bind_symlink_path(
          467  +  sqlite3_stmt *pStmt,            /* Statement to bind to */
          468  +  int iVar,                       /* Variable within pStmt to bind to */
          469  +  const char *zSymlink,           /* Path to symlink */
          470  +  int verboseFlag                 /* If true, show file added */
          471  +){
          472  +  char buf[4096];
          473  +  int n = readlink(zSymlink, buf, sizeof(buf));
          474  +  if( n<0 ){
          475  +    errorMsg("readlink(%s) failed\n", zSymlink);
          476  +  }
          477  +  if( n>sizeof(buf) ){
          478  +    errorMsg("symlinked path is too long (max %d bytes)\n", sizeof(buf));
          479  +  }
          480  +
          481  +  sqlite3_bind_text(pStmt, iVar, buf, n, SQLITE_TRANSIENT);
          482  +  if( verboseFlag ) printf("  added: %s -> %.*s\n", zSymlink, n, buf);
          483  +}
   445    484   
   446    485   /*
   447    486   ** Add a file to the database.
   448    487   */
   449    488   static void add_file(
   450    489     const char *zFilename,     /* Name of file to add */
   451    490     int verboseFlag,           /* If true, show each file added */
................................................................................
   454    493     int rc;
   455    494     struct stat x;
   456    495     int szOrig;
   457    496     int szCompr;
   458    497     const char *zName;
   459    498   
   460    499     check_filename(zFilename);
   461         -  rc = stat(zFilename, &x);
          500  +  rc = lstat(zFilename, &x);
   462    501     if( rc ) errorMsg("no such file or directory: %s\n", zFilename);
   463    502     if( x.st_size>1000000000 ){
   464    503       errorMsg("file too big: %s\n", zFilename);
   465    504     }
   466    505     if( pStmt==0 ){
   467    506       db_prepare("REPLACE INTO sqlar(name,mode,mtime,sz,data)"
   468    507                  " VALUES(?1,?2,?3,?4,?5)");
   469    508     }
   470    509     zName = zFilename;
   471    510     while( zName[0]=='/' ) zName++;
   472    511     sqlite3_bind_text(pStmt, 1, zName, -1, SQLITE_STATIC);
   473    512     sqlite3_bind_int(pStmt, 2, x.st_mode);
   474    513     sqlite3_bind_int64(pStmt, 3, x.st_mtime);
   475         -  if( S_ISREG(x.st_mode) ){
          514  +  if( S_ISLNK(x.st_mode) ){
          515  +    sqlite3_bind_int(pStmt, 4, -1);
          516  +    bind_symlink_path(pStmt, 5, zFilename, verboseFlag);
          517  +  }else if( S_ISREG(x.st_mode) ){
   476    518       char *zContent = read_file(zFilename, &szOrig, &szCompr, noCompress);
   477    519       sqlite3_bind_int(pStmt, 4, szOrig);
   478    520       sqlite3_bind_blob(pStmt, 5, zContent, szCompr, sqlite3_free);
   479    521       if( verboseFlag ){
   480    522         if( szCompr<szOrig ){
   481    523           int pct = szOrig ? (100*(sqlite3_int64)szCompr)/szOrig : 0;
   482    524           printf("  added: %s (deflate %d%%)\n", zFilename, 100-pct);
................................................................................
   586    628     }else if( extractFlag ){
   587    629       const char *zSql;
   588    630       db_open(zArchive, 0, seeFlag, azFiles, nFiles);
   589    631       zSql = "SELECT name, mode, mtime, sz, data FROM sqlar"
   590    632              " WHERE name_on_list(name)";
   591    633       db_prepare(zSql);
   592    634       while( sqlite3_step(pStmt)==SQLITE_ROW ){
          635  +      int sz = sqlite3_column_int(pStmt, 3);     /* Size of file on disk */
          636  +      const char *pData;                         /* Data for this file */
   593    637         const char *zFN = (const char*)sqlite3_column_text(pStmt, 0);
   594    638         check_filename(zFN);
   595    639         if( zFN[0]=='/' ){
   596    640           errorMsg("absolute pathname: %s\n", zFN);
   597    641         }
   598    642         if( sqlite3_column_type(pStmt,4)==SQLITE_BLOB && access(zFN, F_OK)==0 ){
   599    643           errorMsg("file already exists: %s\n", zFN);
   600    644         }
   601    645         if( verboseFlag ) printf("%s\n", zFN);
          646  +      if( sz<0 ){
          647  +        /* symlink */
          648  +        pData = (const char*)sqlite3_column_text(pStmt, 4);
          649  +      }else{
          650  +        pData = (const char*)sqlite3_column_blob(pStmt,4);
          651  +        if( pData==0 && sqlite3_column_type(pStmt, 4)!=SQLITE_NULL ){
          652  +          /* If the file is zero bytes in size, then column "data" will
          653  +          ** contain a zero-byte blob value. In this case, column_blob()
          654  +          ** returns NULL, which confuses the write_file() call into
          655  +          ** thinking this is a directory, not a zero-byte file.  */
          656  +          pData = "";
          657  +        }
          658  +      }
   602    659         write_file(zFN, sqlite3_column_int(pStmt,1),
   603    660                    sqlite3_column_int64(pStmt,2),
   604         -                 sqlite3_column_int(pStmt,3),
   605         -                 sqlite3_column_blob(pStmt,4),
          661  +                 sz, pData,
   606    662                    sqlite3_column_bytes(pStmt,4));
   607    663       }
   608    664       db_close(1);
   609    665     }else{
   610    666       if( azFiles==0 ){
   611    667         errorMsg("Specify one or more files to add on the command-line");
   612    668       }

Changes to sqlarfs.c.

   132    132     sqlite3_bind_text(g.pExists, 1, &path[1], -1, SQLITE_STATIC);
   133    133     rc = sqlite3_step(g.pExists);
   134    134     sqlite3_reset(g.pExists);
   135    135     if( rc==SQLITE_DONE ) return -ENOENT;
   136    136     return 0;
   137    137   }
   138    138   
          139  +static int prepare_read_stmt(void){
          140  +  int rc = SQLITE_OK;
          141  +  if( g.pRead==0 ){
          142  +    rc = sqlite3_prepare_v2(g.db,
          143  +               "SELECT sz, data FROM sqlar WHERE name=?1",
          144  +               -1, &g.pRead, 0);
          145  +  }
          146  +  return rc;
          147  +}
   139    148   
   140    149   /*
   141    150   ** Load the file named path[] into the cache, if it is not there already.
   142    151   **
   143    152   ** Return 0 on success.  Return an error code if the file could not be loaded.
   144    153   */
   145    154   static int loadCache(const char *path){
   146    155     unsigned long int nIn;
   147    156     const char *zIn;
   148         -  int rc;
          157  +  int rc = 0;
   149    158     if( g.zCacheName ){
   150    159       if( strcmp(path, g.zCacheName)==0 ) return 0;
   151    160       sqlite3_free(g.zCacheName); g.zCacheName = 0;
   152    161       sqlite3_free(g.zCacheData); g.zCacheData = 0;
   153    162     }
   154         -  if( g.pRead==0 ){
   155         -    rc = sqlite3_prepare_v2(g.db,
   156         -               "SELECT sz, data FROM sqlar WHERE name=?1",
   157         -               -1, &g.pRead, 0);
   158         -    if( rc!=SQLITE_OK ){
   159         -      return -EIO;
   160         -    }
          163  +  if( prepare_read_stmt() ){
          164  +    return -EIO;
   161    165     }
   162    166     sqlite3_bind_text(g.pRead, 1, path, -1, SQLITE_STATIC);
   163    167     if( sqlite3_step(g.pRead)==SQLITE_ROW ){
   164    168       g.szCache = sqlite3_column_int64(g.pRead, 0);
   165    169       zIn = (const char*)sqlite3_column_blob(g.pRead, 1);
   166    170       nIn = (unsigned long int)sqlite3_column_bytes(g.pRead, 1);
   167         -    g.zCacheData = sqlite3_malloc( g.szCache );
          171  +    g.zCacheData = sqlite3_malloc( g.szCache+1 );
   168    172       if( g.zCacheData==0 ){
   169    173         rc = -EIO;
   170         -    }else{
          174  +    }else if( g.szCache!=nIn ){
   171    175         rc = uncompress((Bytef*)g.zCacheData, &g.szCache, (const Bytef*)zIn, nIn);
   172    176         if( rc!=Z_OK ){
   173    177           sqlite3_free(g.zCacheData);
   174    178           g.zCacheData = 0;
   175    179           rc = -EIO;
   176    180         }
          181  +    }else{
          182  +      memcpy(g.zCacheData, zIn, nIn);
   177    183       }
   178    184       if( g.zCacheData ){
   179    185         g.zCacheName = sqlite3_mprintf("%s", path);
   180    186         if( g.zCacheName==0 ){
   181    187           rc = -EIO;
   182    188           sqlite3_free(g.zCacheData);
   183    189           g.zCacheData = 0;
................................................................................
   208    214       if( offset+size>g.szCache ) size = g.szCache - offset;
   209    215       memcpy(buf, g.zCacheData + offset, size);
   210    216       return size;
   211    217     }else{
   212    218       return rc;
   213    219     }
   214    220   }  
          221  +
          222  +static int sqlarfs_readlink(const char *zSymlink, char *pBuf, size_t nBuf){
          223  +  int rc = 0;
          224  +  if( prepare_read_stmt() ){
          225  +    rc = -EIO;
          226  +  }else{
          227  +    sqlite3_bind_text(g.pRead, 1, &zSymlink[1], -1, SQLITE_STATIC);
          228  +    if( sqlite3_step(g.pRead)==SQLITE_ROW ){
          229  +      int n = sqlite3_column_bytes(g.pRead, 1);
          230  +      if( n<nBuf ){
          231  +        memcpy(pBuf, sqlite3_column_text(g.pRead, 1), n);
          232  +        pBuf[n] = '\0';
          233  +      }else{
          234  +        rc = -ENAMETOOLONG;
          235  +      }
          236  +    }else{
          237  +      rc = -ENOENT;
          238  +    }
          239  +    sqlite3_reset(g.pRead);
          240  +  }
          241  +  return rc;
          242  +}
   215    243   
   216    244   static struct fuse_operations sqlarfs_methods = {
   217         -  .getattr = sqlarfs_getattr,
   218         -  .readdir = sqlarfs_readdir,
   219         -  .open   	= sqlarfs_open,
   220         -  .read    = sqlarfs_read,
          245  +  .getattr  = sqlarfs_getattr,
          246  +  .readdir  = sqlarfs_readdir,
          247  +  .open     = sqlarfs_open,
          248  +  .read     = sqlarfs_read,
          249  +  .readlink = sqlarfs_readlink,
   221    250   };
   222    251   
   223    252   /*
   224    253   ** Show a help message and quit.
   225    254   */
   226    255   static void showHelp(const char *argv0){
   227    256     fprintf(stderr, "Usage: %s [options] archive mount-point\n", argv0);