Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Fix issues on unix with opening database files via symlinks that are not in the current working directory. And with nested symlinks. |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | follow-symlinks |
Files: | files | file ages | folders |
SHA1: |
80398fd44fb232193450103808e1854e |
User & Date: | dan 2016-01-25 17:04:48.546 |
Context
2016-01-25
| ||
18:05 | Simplify the unixFullpathname() function. This adds a dependency on lstat(). (check-in: f71249d3db user: dan tags: follow-symlinks) | |
17:04 | Fix issues on unix with opening database files via symlinks that are not in the current working directory. And with nested symlinks. (check-in: 80398fd44f user: dan tags: follow-symlinks) | |
15:57 | Replace the OP_SetIfNotPos operator with OP_OffsetLimit in the VDBE, for simpler and smaller code. (check-in: 7ac017a498 user: drh tags: trunk) | |
Changes
Changes to src/os_unix.c.
︙ | ︙ | |||
145 146 147 148 149 150 151 152 153 154 155 156 157 158 | #endif /* ** Maximum supported path-length. */ #define MAX_PATHNAME 512 /* Always cast the getpid() return type for compatibility with ** kernel modules in VxWorks. */ #define osGetpid(X) (pid_t)getpid() /* ** Only set the lastErrno if the error code is a real error and not ** a normal expected return code of SQLITE_BUSY or SQLITE_OK | > > > > > | 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 | #endif /* ** Maximum supported path-length. */ #define MAX_PATHNAME 512 /* ** Maximum supported symbolic links */ #define SQLITE_MAX_SYMLINKS 100 /* Always cast the getpid() return type for compatibility with ** kernel modules in VxWorks. */ #define osGetpid(X) (pid_t)getpid() /* ** Only set the lastErrno if the error code is a real error and not ** a normal expected return code of SQLITE_BUSY or SQLITE_OK |
︙ | ︙ | |||
5923 5924 5925 5926 5927 5928 5929 | *pResOut = (0==osStat(zPath, &buf) && buf.st_size>0); }else{ *pResOut = osAccess(zPath, W_OK|R_OK)==0; } return SQLITE_OK; } | < > | > > | | < < | | < | | > | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 5928 5929 5930 5931 5932 5933 5934 5935 5936 5937 5938 5939 5940 5941 5942 5943 5944 5945 5946 5947 5948 5949 5950 5951 5952 5953 5954 5955 5956 5957 5958 5959 5960 5961 5962 5963 5964 5965 5966 5967 5968 5969 5970 5971 5972 5973 5974 5975 5976 5977 5978 5979 5980 5981 5982 5983 5984 5985 5986 5987 5988 5989 5990 5991 5992 5993 5994 5995 5996 5997 5998 5999 6000 6001 6002 6003 6004 6005 6006 6007 6008 6009 6010 6011 6012 6013 6014 6015 6016 6017 6018 6019 6020 6021 6022 6023 6024 6025 6026 6027 6028 6029 6030 6031 6032 6033 6034 6035 6036 6037 6038 6039 6040 6041 6042 6043 6044 6045 6046 6047 6048 6049 6050 6051 6052 6053 6054 6055 6056 6057 6058 6059 6060 6061 6062 6063 6064 6065 6066 6067 6068 6069 6070 6071 6072 6073 6074 6075 6076 6077 6078 6079 6080 6081 6082 6083 6084 6085 6086 | *pResOut = (0==osStat(zPath, &buf) && buf.st_size>0); }else{ *pResOut = osAccess(zPath, W_OK|R_OK)==0; } return SQLITE_OK; } /* ** Buffer zOut contains a (possibly) relative pathname. Overwrite it with ** the corresponding full pathname. ** ** Parameter nOut is the allocated size of buffer zOut. nByte is the number ** of bytes in the nul-terminated string that it contains (not including ** the nul-terminator itself). ** ** Return SQLITE_OK if successful, or an SQLite error code otherwise. */ static int mkFullPathname( const char *zPath, /* Use this path to log any errors */ char *zOut, /* IN/OUT: Buffer to modify */ int nByte, /* size of nul-terminated zOut in bytes */ int nOut /* Allocated size of buffer zOut */ ){ /* If buffer zOut[] now contains an absolute path there is nothing more ** to do. If it contains a relative path, do the following: ** ** * move the relative path string so that it is at the end of th ** zOut[] buffer. ** * Call getcwd() to read the path of the current working directory ** into the start of the zOut[] buffer. ** * Append a '/' character to the cwd string and move the ** relative path back within the buffer so that it immediately ** follows the '/'. ** ** This code is written so that if the combination of the CWD and relative ** path are larger than the allocated size of zOut[] the CWD is silently ** truncated to make it fit. This is Ok, as SQLite refuses to open any ** file for which this function returns a full path larger than (nOut-8) ** bytes in size. */ assert( nByte<nOut ); testcase( nByte==nOut-5 ); testcase( nByte==nOut-4 ); if( zOut[0]!='/' && nByte<nOut-4 ){ int nCwd; int nRem = nOut-nByte-1; memmove(&zOut[nRem], zOut, nByte+1); zOut[nRem-1] = '\0'; if( osGetcwd(zOut, nRem-1)==0 ){ return unixLogError(SQLITE_CANTOPEN_BKPT, "getcwd", zPath); } nCwd = sqlite3Strlen30(zOut); assert( nCwd<=nRem-1 ); zOut[nCwd] = '/'; memmove(&zOut[nCwd+1], &zOut[nRem], nByte+1); } return SQLITE_OK; } /* ** Turn a relative pathname into a full pathname. The relative path ** is stored as a nul-terminated string in the buffer pointed to by ** zPath. ** ** zOut points to a buffer of at least sqlite3_vfs.mxPathname bytes ** (in this case, MAX_PATHNAME bytes). The full-path is written to ** this buffer before returning. */ static int unixFullPathname( sqlite3_vfs *pVfs, /* Pointer to vfs object */ const char *zPath, /* Possibly relative input path */ int nOut, /* Size of output buffer in bytes */ char *zOut /* Output buffer */ ){ #if !defined(HAVE_READLINK) sqlite3_snprintf(nOut, zOut, "%s", zIn); nByte = sqlite3Strlen30(zOut); return mkFullPathname(zPath, zOut, sqlite3Strlen30(zOut), nOut); #else int rc = SQLITE_OK; int nByte; int nLink = 0; /* Number of symbolic links followed so far */ int bLink; /* True for a symbolic link */ const char *zIn = zPath; /* Input path for each iteration of loop */ char *zDel = 0; assert( pVfs->mxPathname==MAX_PATHNAME ); UNUSED_PARAMETER(pVfs); /* It's odd to simulate an io-error here, but really this is just ** using the io-error infrastructure to test that SQLite handles this ** function failing. This function could fail if, for example, the ** current working directory has been unlinked. */ SimulateIOError( return SQLITE_ERROR ); do { /* Attempt to resolve the path as if it were a symbolic link. If it is ** a symbolic link, the resolved path is stored in buffer zOut[]. Or, if ** the identified file is not a symbolic link or does not exist, then ** zPath is copied directly into zOut. Either way, nByte is left set to ** the size of the string copied into zOut[] in bytes. */ assert( (zDel==0 && zIn==zPath) || (zDel!=0 && zIn==zDel) ); if( zDel ){ assert( zIn==zDel ); sqlite3_snprintf(nOut, zDel, "%s", zOut); } nByte = osReadlink(zIn, zOut, nOut-1); if( nByte<0 ){ if( errno!=EINVAL && errno!=ENOENT ){ rc = unixLogError(SQLITE_CANTOPEN_BKPT, "readlink", zPath); }else{ sqlite3_snprintf(nOut, zOut, "%s", zIn); nByte = sqlite3Strlen30(zOut); } bLink = 0; }else if( ++nLink>SQLITE_MAX_SYMLINKS ){ sqlite3_log(SQLITE_CANTOPEN, "too many symbolic links (max=%d)", SQLITE_MAX_SYMLINKS ); rc = SQLITE_CANTOPEN_BKPT; }else{ zOut[nByte] = '\0'; if( zOut[0]!='/' ){ int n; for(n = sqlite3Strlen30(zIn); n>0 && zIn[n-1]!='/'; n--); if( nByte+n+1>nOut ){ rc = SQLITE_CANTOPEN_BKPT; }else{ memmove(&zOut[n], zOut, nByte+1); memcpy(zOut, zIn, n); nByte += n; } } if( zDel==0 ){ zDel = sqlite3_malloc(nOut); if( zDel==0 ) rc = SQLITE_NOMEM; zIn = (const char*)zDel; } bLink = 1; } if( rc==SQLITE_OK ){ rc = mkFullPathname(zPath, zOut, nByte, nOut); } }while( bLink && rc==SQLITE_OK ); sqlite3_free(zDel); return rc; #endif /* HAVE_READLINK */ } #ifndef SQLITE_OMIT_LOAD_EXTENSION /* ** Interfaces for opening a shared library, finding entry points ** within the shared library, and closing the shared library. */ |
︙ | ︙ |
Changes to test/symlink.test.
︙ | ︙ | |||
79 80 81 82 83 84 85 | #------------------------------------------------------------------------- # Test that journal and wal files are created next to the real file, # not the symlink. # do_test 2.0 { catch { db close } catch { db2 close } | | > > > > > > | | | < | | | | | | | | | | | | | | | | | | | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 | #------------------------------------------------------------------------- # Test that journal and wal files are created next to the real file, # not the symlink. # do_test 2.0 { catch { db close } catch { db2 close } forcedelete test.db test.db2 test.db3 sqlite3 db test.db execsql { CREATE TABLE t1(x) } file link test.db2 test.db file link test.db3 test.db2 set {} {} } {} foreach {tn f} {1 test.db2 2 test.db3} { do_test 2.$tn.1 { sqlite3 db2 $f file exists test.db-journal } 0 do_test 2.$tn.2 { execsql { BEGIN; INSERT INTO t1 VALUES(1); } db2 file exists test.db-journal } 1 do_test 2.$tn.3 { list [file exists test2.db-journal] [file exists test3.db-journal] } {0 0} do_test 2.$tn.4 { execsql { COMMIT; PRAGMA journal_mode = wal; INSERT INTO t1 VALUES(2); } db2 file exists test.db-wal } 1 do_test 2.$tn.5 { list [file exists test2.db-wal] [file exists test3.db-wal] } {0 0} do_execsql_test 2.$tn.6 { SELECT * FROM t1; } {1 2} db2 close do_execsql_test 2.$tn.7 { DELETE FROM t1; PRAGMA journal_mode = delete; } delete } # Try to open a ridiculously long pathname. Bug found by # Kostya Serebryany using libFuzzer on 2015-11-30. # do_test 3.1 { db close catch {sqlite3 db [string repeat [string repeat x 100]/ 6]} res set res } {unable to open database file} #------------------------------------------------------------------------- # Test that relative symlinks that are not located in the cwd work. # do_test 4.1 { forcedelete x y z file mkdir x file mkdir y file mkdir z sqlite3 db x/test.db file link y/test.db ../x/test.db file link z/test.db ../y/test.db execsql { PRAGMA journal_mode = wal; CREATE TABLE t1(x, y); INSERT INTO t1 VALUES('hello', 'world'); } } {wal} do_test 4.2.1 { db close sqlite3 db y/test.db db eval { SELECT * FROM t1 } } {hello world} do_test 4.2.2 { list [file exists x/test.db-wal] [file exists y/test.db-wal] } {1 0} do_test 4.3.1 { db close sqlite3 db z/test.db db eval { SELECT * FROM t1 } } {hello world} do_test 4.3.2 { list [file exists x/test.db-wal] [file exists y/test.db-wal] \ [file exists z/test.db-wal] } {1 0 0} do_test 4.4.0 { forcedelete w file mkdir w file link w/test.db [file join [pwd] x/test.db] set {} {} } {} do_test 4.4.1 { db close breakpoint sqlite3 db w/test.db db eval { SELECT * FROM t1 } } {hello world} do_test 4.4.2 { list [file exists x/test.db-wal] [file exists w/test.db-wal] } {1 0} finish_test |