1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-11-12 13:01:09 +03:00

Fix issues on unix with opening database files via symlinks that are not in the current working directory. And with nested symlinks.

FossilOrigin-Name: 80398fd44fb232193450103808e1854e0eba5652
This commit is contained in:
dan
2016-01-25 17:04:48 +00:00
parent cc2fa4cf55
commit e88ec187cd
4 changed files with 215 additions and 79 deletions

View File

@@ -1,5 +1,5 @@
C Replace\sthe\sOP_SetIfNotPos\soperator\swith\sOP_OffsetLimit\sin\sthe\sVDBE,\sfor\nsimpler\sand\ssmaller\scode. C Fix\sissues\son\sunix\swith\sopening\sdatabase\sfiles\svia\ssymlinks\sthat\sare\snot\sin\sthe\scurrent\sworking\sdirectory.\sAnd\swith\snested\ssymlinks.
D 2016-01-25T15:57:29.060 D 2016-01-25T17:04:48.546
F Makefile.in 027c1603f255390c43a426671055a31c0a65fdb4 F Makefile.in 027c1603f255390c43a426671055a31c0a65fdb4
F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
F Makefile.msc 1708a78eda223b6daa302b140037fcc214a779f9 F Makefile.msc 1708a78eda223b6daa302b140037fcc214a779f9
@@ -329,7 +329,7 @@ F src/os.c 8fd25588eeba74068d41102d26810e216999b6c8
F src/os.h 3e57a24e2794a94d3cf2342c6d9a884888cd96bf F src/os.h 3e57a24e2794a94d3cf2342c6d9a884888cd96bf
F src/os_common.h abdb9a191a367793268fe553d25bab894e986a0e F src/os_common.h abdb9a191a367793268fe553d25bab894e986a0e
F src/os_setup.h c9d4553b5aaa6f73391448b265b89bed0b890faa F src/os_setup.h c9d4553b5aaa6f73391448b265b89bed0b890faa
F src/os_unix.c 0eb7f469fcd4e1fbedf30060438e26b839ec5486 F src/os_unix.c cf524029242b4f878d6b97bad25cc2c0b66c2b31
F src/os_win.c 386fba30419e8458b13209781c2af5590eab2811 F src/os_win.c 386fba30419e8458b13209781c2af5590eab2811
F src/os_win.h eb7a47aa17b26b77eb97e4823f20a00b8bda12ca F src/os_win.h eb7a47aa17b26b77eb97e4823f20a00b8bda12ca
F src/pager.c 2916c66aee50f69d9ec56a7619b62d9c6a3bee61 F src/pager.c 2916c66aee50f69d9ec56a7619b62d9c6a3bee61
@@ -1061,7 +1061,7 @@ F test/subselect.test d24fd8757daf97dafd2e889c73ea4c4272dcf4e4
F test/substr.test 18f57c4ca8a598805c4d64e304c418734d843c1a F test/substr.test 18f57c4ca8a598805c4d64e304c418734d843c1a
F test/subtype1.test 7fe09496352f97053af1437150751be2d0a0cae8 F test/subtype1.test 7fe09496352f97053af1437150751be2d0a0cae8
F test/superlock.test 1cde669f68d2dd37d6c9bd35eee1d95491ae3fc2 F test/superlock.test 1cde669f68d2dd37d6c9bd35eee1d95491ae3fc2
F test/symlink.test cbf6cb8c6c4b63a39e9f0f6b0d5c99e249dbc102 F test/symlink.test 511db82662446bb0d3619002422760ef8e4b1122
F test/sync.test a34cd43e98b7fb84eabbf38f7ed8f7349b3f3d85 F test/sync.test a34cd43e98b7fb84eabbf38f7ed8f7349b3f3d85
F test/syscall.test 2aa9e111b79fb385681ff8940124def6f8faab87 F test/syscall.test 2aa9e111b79fb385681ff8940124def6f8faab87
F test/sysfault.test c9f2b0d8d677558f74de750c75e12a5454719d04 F test/sysfault.test c9f2b0d8d677558f74de750c75e12a5454719d04
@@ -1419,7 +1419,10 @@ F tool/vdbe_profile.tcl 246d0da094856d72d2c12efec03250d71639d19f
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
F tool/warnings.sh 48bd54594752d5be3337f12c72f28d2080cb630b F tool/warnings.sh 48bd54594752d5be3337f12c72f28d2080cb630b
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
P 30671345b1c1ee55a2d1aa17273213f1849efd81 P 7ac017a498b6fb28343eef2d24e400c7800660d6
R f342889ba3409bd63b1cf54d88bfe0e2 R 0289127606d03bb3b649ecbd39bb14a5
U drh T *branch * follow-symlinks
Z e7c5fc86067d4269675714897cec0b51 T *sym-follow-symlinks *
T -sym-trunk *
U dan
Z 3e18b3d01ba737d3635110839e378935

View File

@@ -1 +1 @@
7ac017a498b6fb28343eef2d24e400c7800660d6 80398fd44fb232193450103808e1854e0eba5652

View File

@@ -149,6 +149,11 @@
*/ */
#define MAX_PATHNAME 512 #define MAX_PATHNAME 512
/*
** Maximum supported symbolic links
*/
#define SQLITE_MAX_SYMLINKS 100
/* Always cast the getpid() return type for compatibility with /* Always cast the getpid() return type for compatibility with
** kernel modules in VxWorks. */ ** kernel modules in VxWorks. */
#define osGetpid(X) (pid_t)getpid() #define osGetpid(X) (pid_t)getpid()
@@ -5927,52 +5932,22 @@ static int unixAccess(
return SQLITE_OK; return SQLITE_OK;
} }
/* /*
** Turn a relative pathname into a full pathname. The relative path ** Buffer zOut contains a (possibly) relative pathname. Overwrite it with
** is stored as a nul-terminated string in the buffer pointed to by ** the corresponding full pathname.
** zPath.
** **
** zOut points to a buffer of at least sqlite3_vfs.mxPathname bytes ** Parameter nOut is the allocated size of buffer zOut. nByte is the number
** (in this case, MAX_PATHNAME bytes). The full-path is written to ** of bytes in the nul-terminated string that it contains (not including
** this buffer before returning. ** the nul-terminator itself).
**
** Return SQLITE_OK if successful, or an SQLite error code otherwise.
*/ */
static int unixFullPathname( static int mkFullPathname(
sqlite3_vfs *pVfs, /* Pointer to vfs object */ const char *zPath, /* Use this path to log any errors */
const char *zPath, /* Possibly relative input path */ char *zOut, /* IN/OUT: Buffer to modify */
int nOut, /* Size of output buffer in bytes */ int nByte, /* size of nul-terminated zOut in bytes */
char *zOut /* Output buffer */ int nOut /* Allocated size of buffer zOut */
){ ){
int nByte;
/* 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 );
assert( pVfs->mxPathname==MAX_PATHNAME );
UNUSED_PARAMETER(pVfs);
#if defined(HAVE_READLINK)
/* 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. */
nByte = osReadlink(zPath, zOut, nOut-1);
if( nByte<0 ){
if( errno!=EINVAL && errno!=ENOENT ){
return unixLogError(SQLITE_CANTOPEN_BKPT, "readlink", zPath);
}
sqlite3_snprintf(nOut, zOut, "%s", zPath);
nByte = sqlite3Strlen30(zOut);
}else{
zOut[nByte] = '\0';
}
#endif
/* If buffer zOut[] now contains an absolute path there is nothing more /* If buffer zOut[] now contains an absolute path there is nothing more
** to do. If it contains a relative path, do the following: ** to do. If it contains a relative path, do the following:
** **
@@ -5989,6 +5964,7 @@ static int unixFullPathname(
** truncated to make it fit. This is Ok, as SQLite refuses to open any ** 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) ** file for which this function returns a full path larger than (nOut-8)
** bytes in size. */ ** bytes in size. */
assert( nByte<nOut );
testcase( nByte==nOut-5 ); testcase( nByte==nOut-5 );
testcase( nByte==nOut-4 ); testcase( nByte==nOut-4 );
if( zOut[0]!='/' && nByte<nOut-4 ){ if( zOut[0]!='/' && nByte<nOut-4 ){
@@ -6008,6 +5984,100 @@ static int unixFullPathname(
return SQLITE_OK; 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 #ifndef SQLITE_OMIT_LOAD_EXTENSION
/* /*

View File

@@ -83,38 +83,49 @@ do_test 1.5 {
do_test 2.0 { do_test 2.0 {
catch { db close } catch { db close }
catch { db2 close } catch { db2 close }
forcedelete test.db test.db2 forcedelete test.db test.db2 test.db3
sqlite3 db test.db sqlite3 db test.db
execsql { CREATE TABLE t1(x) } execsql { CREATE TABLE t1(x) }
file link test.db2 test.db file link test.db2 test.db
sqlite3 db2 test.db2 file link test.db3 test.db2
file exists test.db-journal set {} {}
} 0 } {}
do_test 2.1 { 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 { execsql {
BEGIN; BEGIN;
INSERT INTO t1 VALUES(1); INSERT INTO t1 VALUES(1);
} db2 } db2
file exists test.db-journal file exists test.db-journal
} 1 } 1
do_test 2.2 { do_test 2.$tn.3 {
file exists test.db2-journal list [file exists test2.db-journal] [file exists test3.db-journal]
} 0 } {0 0}
do_test 2.3 { do_test 2.$tn.4 {
execsql { execsql {
COMMIT; COMMIT;
PRAGMA journal_mode = wal; PRAGMA journal_mode = wal;
INSERT INTO t1 VALUES(2); INSERT INTO t1 VALUES(2);
} db2 } db2
file exists test.db-wal file exists test.db-wal
} 1 } 1
do_test 2.4 { do_test 2.$tn.5 {
file exists test.db2-wal list [file exists test2.db-wal] [file exists test3.db-wal]
} 0 } {0 0}
do_execsql_test 2.5 { do_execsql_test 2.$tn.6 {
SELECT * FROM t1; SELECT * FROM t1;
} {1 2} } {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 # Try to open a ridiculously long pathname. Bug found by
# Kostya Serebryany using libFuzzer on 2015-11-30. # Kostya Serebryany using libFuzzer on 2015-11-30.
@@ -125,5 +136,57 @@ do_test 3.1 {
set res set res
} {unable to open database file} } {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 finish_test