From: dan Date: Sat, 31 Oct 2015 17:58:33 +0000 (+0000) Subject: On unix, if a file is opened via a symlink, create, read and write journal and wal... X-Git-Tag: version-3.10.0~179^2 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=245fdc60d645214b745542b8f73d6efbc556d143;p=thirdparty%2Fsqlite.git On unix, if a file is opened via a symlink, create, read and write journal and wal files based on the name of the actual db file, not the symlink. FossilOrigin-Name: c7c8105099c0412ac6c605f98987092c10bde57c --- diff --git a/manifest b/manifest index b9fdab8d24..683c05afd8 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\smutex\soperations\sto\stest\scode\sin\stest3.c\sto\savoid\striggering\sassert()\sfailures\sin\scertain\sconfigurations. -D 2015-10-30T20:54:25.598 +C On\sunix,\sif\sa\sfile\sis\sopened\svia\sa\ssymlink,\screate,\sread\sand\swrite\sjournal\sand\swal\sfiles\sbased\son\sthe\sname\sof\sthe\sactual\sdb\sfile,\snot\sthe\ssymlink. +D 2015-10-31T17:58:33.378 F Makefile.in 4469ed8b02a9934fea9503d791165367d19db2f7 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc 702d3e98f3afc6587a78481257f3c4c900efc3a4 @@ -324,7 +324,7 @@ F src/os.c 8fd25588eeba74068d41102d26810e216999b6c8 F src/os.h 3e57a24e2794a94d3cf2342c6d9a884888cd96bf F src/os_common.h abdb9a191a367793268fe553d25bab894e986a0e F src/os_setup.h c9d4553b5aaa6f73391448b265b89bed0b890faa -F src/os_unix.c fc93d55f96bb978f0b0168c6ea7d6fc60b0e172c +F src/os_unix.c cf72e06e15839ebe7121e01d3eebf256c039b0ca F src/os_win.c 1716291e5ec2dbfc5a1fe0b32182030f1f7d8acf F src/os_win.h eb7a47aa17b26b77eb97e4823f20a00b8bda12ca F src/pager.c 9c1eec0d88133484b165fa0b5284a411c24b964c @@ -913,7 +913,7 @@ F test/orderby6.test 8b38138ab0972588240b3fca0985d2e400432859 F test/orderby7.test 3d1383d52ade5b9eb3a173b3147fdd296f0202da F test/orderby8.test 23ef1a5d72bd3adcc2f65561c654295d1b8047bd F test/orderby9.test 87fb9548debcc2cd141c5299002dd94672fa76a3 -F test/oserror.test 14fec2796c2b6fe431c7823750e8a18a761176d7 +F test/oserror.test 361346396ae18462c7393c1ac5c3f17237bd89b2 F test/ovfl.test 4f7ca651cba5c059a12d8c67dddd49bec5747799 F test/pager1.test 1acbdb14c5952a72dd43129cabdbf69aaa3ed1fa F test/pager2.test 67b8f40ae98112bcdba1f2b2d03ea83266418c71 @@ -1046,8 +1046,9 @@ F test/subselect.test d24fd8757daf97dafd2e889c73ea4c4272dcf4e4 F test/substr.test 18f57c4ca8a598805c4d64e304c418734d843c1a F test/subtype1.test 7fe09496352f97053af1437150751be2d0a0cae8 F test/superlock.test 1cde669f68d2dd37d6c9bd35eee1d95491ae3fc2 +F test/symlink.test 2513f7c030df0f435c6415687ba8b739f3d312df F test/sync.test a34cd43e98b7fb84eabbf38f7ed8f7349b3f3d85 -F test/syscall.test d2fdaad713f103ac611fe7ef9b724c7b69f8149c +F test/syscall.test fba9ebdc6905d05bba6a835e691f20ed9ea2cc88 F test/sysfault.test fa776e60bf46bdd3ae69f0b73e46ee3977a58ae6 F test/tabfunc01.test 03c4ad422c6ab596cff6dcaf86dd061a9f039525 F test/table.test b708f3e5fa2542fa51dfab21fc07b36ea445cb2f @@ -1397,7 +1398,10 @@ F tool/vdbe_profile.tcl 246d0da094856d72d2c12efec03250d71639d19f F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh 48bd54594752d5be3337f12c72f28d2080cb630b F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f -P 7565b046ff939e8310631397a4526fbd98b99aaf -R 614298574df997c982bceb820bb362b3 +P 9f19420b0a79dff65fc3a9d548f4b3fc4955f9f9 +R 6c3cb8b317a5bfe5a7c51bf7e4e6022e +T *branch * follow-symlinks +T *sym-follow-symlinks * +T -sym-trunk * U dan -Z ee268c45e17adbdf740dc7b91893f898 +Z 9a079ec9742e185ced0424071f7f7c65 diff --git a/manifest.uuid b/manifest.uuid index 4eeefb872e..9abe98cb17 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -9f19420b0a79dff65fc3a9d548f4b3fc4955f9f9 \ No newline at end of file +c7c8105099c0412ac6c605f98987092c10bde57c \ No newline at end of file diff --git a/src/os_unix.c b/src/os_unix.c index fb2a8ca916..b322d238e8 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -464,6 +464,9 @@ static struct unix_syscall { { "getpagesize", (sqlite3_syscall_ptr)unixGetpagesize, 0 }, #define osGetpagesize ((int(*)(void))aSyscall[24].pCurrent) + { "readlink", (sqlite3_syscall_ptr)readlink, 0 }, +#define osReadlink ((ssize_t(*)(const char*,char*,size_t))aSyscall[25].pCurrent) + #endif }; /* End of the overrideable system calls */ @@ -6026,6 +6029,7 @@ static int unixFullPathname( int nOut, /* Size of output buffer in bytes */ char *zOut /* Output buffer */ ){ + 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 @@ -6037,17 +6041,53 @@ static int unixFullPathname( assert( pVfs->mxPathname==MAX_PATHNAME ); UNUSED_PARAMETER(pVfs); - zOut[nOut-1] = '\0'; - if( zPath[0]=='/' ){ - sqlite3_snprintf(nOut, zOut, "%s", zPath); + /* 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); + } + zOut[nOut-1] = '\0'; + sqlite3_snprintf(nOut-1, zOut, "%s", zPath); + nByte = sqlite3Strlen30(zOut); }else{ + zOut[nByte] = '\0'; + } + + /* 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. */ + if( zOut[0]!='/' ){ int nCwd; - if( osGetcwd(zOut, nOut-1)==0 ){ + 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 = (int)strlen(zOut); - sqlite3_snprintf(nOut-nCwd, &zOut[nCwd], "/%s", zPath); + nCwd = sqlite3Strlen30(zOut); + assert( nCwd<=nRem-1 ); + zOut[nCwd] = '/'; + memmove(&zOut[nCwd+1], &zOut[nRem], nByte+1); } + return SQLITE_OK; } @@ -7542,7 +7582,7 @@ int sqlite3_os_init(void){ /* Double-check that the aSyscall[] array has been constructed ** correctly. See ticket [bb3a86e890c8e96ab] */ - assert( ArraySize(aSyscall)==25 ); + assert( ArraySize(aSyscall)==26 ); /* Register all VFSes defined in the aVfs[] array */ for(i=0; i<(sizeof(aVfs)/sizeof(sqlite3_vfs)); i++){ diff --git a/test/oserror.test b/test/oserror.test index 1bfa37cd6c..5fa7f98eb5 100644 --- a/test/oserror.test +++ b/test/oserror.test @@ -93,7 +93,9 @@ do_test 1.4.1 { list [catch { sqlite3 dbh /root/test.db } msg] $msg } {1 {unable to open database file}} -do_re_test 1.4.2 { lindex $::log 0 } {^os_unix.c:\d*: \(\d+\) open\(.*test.db\) - } +do_re_test 1.4.2 { + lindex $::log 0 +} {^os_unix.c:\d*: \(\d+\) (open|readlink)\(.*test.db\) - } #-------------------------------------------------------------------------- # Tests oserror-1.* test failures in the unlink() system call. diff --git a/test/symlink.test b/test/symlink.test new file mode 100644 index 0000000000..af7ec2a67b --- /dev/null +++ b/test/symlink.test @@ -0,0 +1,119 @@ +# 2015 October 31 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# This file implements regression tests for SQLite library. The +# focus of this file is testing that SQLite can follow symbolic links. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set testprefix symlink + +# This only runs on unix. +if {$::tcl_platform(platform)!="unix"} { + finish_test + return +} + +# Ensure that test.db has been created. +# +do_execsql_test 1.0 { + CREATE TABLE t1(x, y); +} + +# Test that SQLite follows symlinks when opening files. +# +forcedelete test.db2 +do_test 1.1 { + file link test.db2 test.db + sqlite3 db2 test.db2 + sqlite3_db_filename db2 main +} [file join [pwd] test.db] + +# Test that if the symlink points to a file that does not exists, it is +# created when it is opened. +# +do_test 1.2.1 { + db2 close + db close + forcedelete test.db + file exists test.db +} 0 +do_test 1.2.2 { + sqlite3 db2 test.db2 + file exists test.db +} 1 +do_test 1.2.3 { + sqlite3_db_filename db2 main +} [file join [pwd] test.db] +db2 close + +# Test that a loop of symlinks cannot be opened. +# +do_test 1.3 { + forcedelete test.db + # Note: Tcl [file link] command is too smart to create loops of symlinks. + exec ln -s test.db2 test.db + list [catch { sqlite3 db test.db } msg] $msg +} {1 {unable to open database file}} + +# Test that overly large paths cannot be opened. +# +do_test 1.4 { + set name "test.db[string repeat x 502]" + list [catch { sqlite3 db $name } msg] $msg +} {1 {unable to open database file}} +do_test 1.5 { + set r [expr 510 - [string length test.db] - [string length [pwd]]] + set name "test.db[string repeat x $r]" + list [catch { sqlite3 db $name } msg] $msg +} {1 {unable to open database file}} + +#------------------------------------------------------------------------- +# 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 + sqlite3 db test.db + execsql { CREATE TABLE t1(x) } + file link test.db2 test.db + sqlite3 db2 test.db2 + file exists test.db-journal +} 0 + +do_test 2.1 { + execsql { + BEGIN; + INSERT INTO t1 VALUES(1); + } db2 + file exists test.db-journal +} 1 +do_test 2.2 { + file exists test.db2-journal +} 0 +do_test 2.3 { + execsql { + COMMIT; + PRAGMA journal_mode = wal; + INSERT INTO t1 VALUES(2); + } db2 + file exists test.db-wal +} 1 +do_test 2.4 { + file exists test.db2-wal +} 0 +do_execsql_test 2.5 { + SELECT * FROM t1; +} {1 2} + +finish_test diff --git a/test/syscall.test b/test/syscall.test index c2d9979031..83b8b8b40f 100644 --- a/test/syscall.test +++ b/test/syscall.test @@ -61,7 +61,7 @@ foreach s { fcntl read pread write pwrite fchmod fallocate pread64 pwrite64 unlink openDirectory mkdir rmdir statvfs fchown umask mmap munmap mremap - getpagesize + getpagesize readlink } { if {[test_syscall exists $s]} {lappend syscall_list $s} }