/ Check-in [6d5ce3ed]
Login

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

Overview
Comment: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.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 6d5ce3ede4c7038c19a77268a5a7b9d5650933c2
User & Date: dan 2015-11-02 15:08:56
Context
2015-11-03
14:49
Update the releasetest.tcl script so that it can run multiple tests in parallel in separate processes. check-in: e3de8291 user: drh tags: trunk
2015-11-02
18:57
Merge all recent enhancements and fixes from trunk. check-in: 0546d1cd user: drh tags: sessions
15:10
Merge latest trunk changes, including the follow-symlinks functionality and 3.9.2 bugfixes, into this branch. check-in: 447521d7 user: dan tags: apple-osx
15:08
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. check-in: 6d5ce3ed user: dan tags: trunk
2015-11-01
21:19
If a table-constraint PRIMARY KEY lists a single column in single-quotes and that column has type INTEGER, then make that column an integer primary key, for historical compatibility. Fix for ticket [ac661962a2aeab3c331]. check-in: db319a03 user: drh tags: trunk
2015-10-31
17:58
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. Closed-Leaf check-in: c7c81050 user: dan tags: follow-symlinks
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/os_unix.c.

460
461
462
463
464
465
466



467
468
469
470
471
472
473
....
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
....
7538
7539
7540
7541
7542
7543
7544
7545
7546
7547
7548
7549
7550
7551
7552
#else
  { "mremap",       (sqlite3_syscall_ptr)0,               0 },
#endif
#define osMremap ((void*(*)(void*,size_t,size_t,int,...))aSyscall[23].pCurrent)
  { "getpagesize",  (sqlite3_syscall_ptr)unixGetpagesize, 0 },
#define osGetpagesize ((int(*)(void))aSyscall[24].pCurrent)




#endif

}; /* End of the overrideable system calls */

/*
** This is the xSetSystemCall() method of sqlite3_vfs for all of the
** "unix" VFSes.  Return SQLITE_OK opon successfully updating the
................................................................................
*/
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 */
){


  /* 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);











  zOut[nOut-1] = '\0';
  if( zPath[0]=='/' ){
    sqlite3_snprintf(nOut, zOut, "%s", zPath);

  }else{




















    int nCwd;



    if( osGetcwd(zOut, nOut-1)==0 ){
      return unixLogError(SQLITE_CANTOPEN_BKPT, "getcwd", zPath);
    }
    nCwd = (int)strlen(zOut);
    sqlite3_snprintf(nOut-nCwd, &zOut[nCwd], "/%s", zPath);




  }

  return SQLITE_OK;
}


#ifndef SQLITE_OMIT_LOAD_EXTENSION
/*
** Interfaces for opening a shared library, finding entry points
................................................................................
    UNIXVFS("unix-proxy",    proxyIoFinder ),
#endif
  };
  unsigned int i;          /* Loop counter */

  /* Double-check that the aSyscall[] array has been constructed
  ** correctly.  See ticket [bb3a86e890c8e96ab] */
  assert( ArraySize(aSyscall)==25 );

  /* Register all VFSes defined in the aVfs[] array */
  for(i=0; i<(sizeof(aVfs)/sizeof(sqlite3_vfs)); i++){
    sqlite3_vfs_register(&aVfs[i], i==0);
  }
  return SQLITE_OK; 
}







>
>
>







 







>











>
>
>
>
>
>
>
>
>
>
|
<
|
>

>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

>
>
>
|


<
<
>
>
>
>

>







 







|







460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
....
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
6087
6088
6089
6090
6091
6092
6093
6094
6095
6096
6097
....
7578
7579
7580
7581
7582
7583
7584
7585
7586
7587
7588
7589
7590
7591
7592
#else
  { "mremap",       (sqlite3_syscall_ptr)0,               0 },
#endif
#define osMremap ((void*(*)(void*,size_t,size_t,int,...))aSyscall[23].pCurrent)
  { "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 */

/*
** This is the xSetSystemCall() method of sqlite3_vfs for all of the
** "unix" VFSes.  Return SQLITE_OK opon successfully updating the
................................................................................
*/
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 */
){
  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);

  /* 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;
    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;
}


#ifndef SQLITE_OMIT_LOAD_EXTENSION
/*
** Interfaces for opening a shared library, finding entry points
................................................................................
    UNIXVFS("unix-proxy",    proxyIoFinder ),
#endif
  };
  unsigned int i;          /* Loop counter */

  /* Double-check that the aSyscall[] array has been constructed
  ** correctly.  See ticket [bb3a86e890c8e96ab] */
  assert( ArraySize(aSyscall)==26 );

  /* Register all VFSes defined in the aVfs[] array */
  for(i=0; i<(sizeof(aVfs)/sizeof(sqlite3_vfs)); i++){
    sqlite3_vfs_register(&aVfs[i], i==0);
  }
  return SQLITE_OK; 
}

Changes to test/oserror.test.

89
90
91
92
93
94
95


96
97
98
99
100
101
102
103
# Test a failure in open() due to the path not existing.
#
do_test 1.4.1 {
  set ::log [list]
  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\) - }

#--------------------------------------------------------------------------
# Tests oserror-1.* test failures in the unlink() system call.
#
ifcapable wal {
  do_test 2.1.1 {
    set ::log [list]







>
>
|







89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# Test a failure in open() due to the path not existing.
#
do_test 1.4.1 {
  set ::log [list]
  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|readlink)\(.*test.db\) - }

#--------------------------------------------------------------------------
# Tests oserror-1.* test failures in the unlink() system call.
#
ifcapable wal {
  do_test 2.1.1 {
    set ::log [list]

Added test/symlink.test.















































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
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
# 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

Changes to test/syscall.test.

57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# Tests for the xNextSystemCall method.
#
foreach s {
    open close access getcwd stat fstat ftruncate
    fcntl read pread write pwrite fchmod fallocate
    pread64 pwrite64 unlink openDirectory mkdir rmdir 
    statvfs fchown umask mmap munmap mremap
    getpagesize
} {
  if {[test_syscall exists $s]} {lappend syscall_list $s}
}
do_test 3.1 { lsort [test_syscall list] } [lsort $syscall_list]

#-------------------------------------------------------------------------
# This test verifies that if a call to open() fails and errno is set to







|







57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# Tests for the xNextSystemCall method.
#
foreach s {
    open close access getcwd stat fstat ftruncate
    fcntl read pread write pwrite fchmod fallocate
    pread64 pwrite64 unlink openDirectory mkdir rmdir 
    statvfs fchown umask mmap munmap mremap
    getpagesize readlink
} {
  if {[test_syscall exists $s]} {lappend syscall_list $s}
}
do_test 3.1 { lsort [test_syscall list] } [lsort $syscall_list]

#-------------------------------------------------------------------------
# This test verifies that if a call to open() fails and errno is set to