Fossil

Check-in [912a0d50]
Login

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

Overview
Comment:Make sure that symlinks to directories are honored when dealing with checkout files.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1:912a0d500884dff12593880b7f76b1283c2e14ee
User & Date: mistachkin 2016-10-04 04:49:21
Context
2016-10-04
14:55
Update the built-in SQLite 3.15.0 beta. check-in: 3d8a41f1 user: drh tags: trunk
04:49
Make sure that symlinks to directories are honored when dealing with checkout files. check-in: 912a0d50 user: mistachkin tags: trunk
03:55
Return an error instead of silently ignoring invalid input. check-in: 274e537b user: andybradford tags: trunk
2016-10-03
21:30
Merge updates from trunk. Closed-Leaf check-in: be316701 user: mistachkin tags: dirSymlinks
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/diffcmd.c.

920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
                      diffFlags, pFileDir);
  }
  if( pFileDir ){
    int i;
    for(i=0; pFileDir[i].zName; i++){
      if( pFileDir[i].nUsed==0
       && strcmp(pFileDir[0].zName,".")!=0
       && !file_isdir(g.argv[i+2])
      ){
        fossil_fatal("not found: '%s'", g.argv[i+2]);
      }
      fossil_free(pFileDir[i].zName);
    }
    fossil_free(pFileDir);
  }







|







920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
                      diffFlags, pFileDir);
  }
  if( pFileDir ){
    int i;
    for(i=0; pFileDir[i].zName; i++){
      if( pFileDir[i].nUsed==0
       && strcmp(pFileDir[0].zName,".")!=0
       && !file_wd_isdir(g.argv[i+2])
      ){
        fossil_fatal("not found: '%s'", g.argv[i+2]);
      }
      fossil_free(pFileDir[i].zName);
    }
    fossil_free(pFileDir);
  }

Changes to src/file.c.

291
292
293
294
295
296
297
298



299
300
301

302
303
304
305
306
307








308
309

310
311


312
313
314
315
316
317
318
...
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
  }else{
    rc = getStat(0, 0);
  }
  return rc ? 0 : (S_ISDIR(fileStat.st_mode) ? 1 : 2);
}

/*
** Same as file_isdir(), but takes into account symlinks.



*/
int file_wd_isdir(const char *zFilename){
  int rc;


  if( zFilename ){
    char *zFN = mprintf("%s", zFilename);
    file_simplify_name(zFN, -1, 0);
    rc = getStat(zFN, 1);
    free(zFN);








  }else{
    rc = getStat(0, 1);

  }
  return rc ? 0 : (S_ISDIR(fileStat.st_mode) ? 1 : 2);


}


/*
** Wrapper around the access() system call.
*/
int file_access(const char *zFilename, int flags){
................................................................................
      /*
      ** On Windows, local path looks like: C:/develop/project/file.txt
      ** The if stops us from trying to create a directory of a drive letter
      ** C: in this example.
      */
      if( !(i==2 && zName[1]==':') ){
#endif
        if( file_mkdir(zName, forceFlag) && file_isdir(zName)!=1 ){
          if (errorReturn <= 0) {
            fossil_fatal_recursive("unable to create directory %s", zName);
          }
          rc = errorReturn;
          break;
        }
#if defined(_WIN32) || defined(__CYGWIN__)







|
>
>
>



>

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

<
>

<
>
>







 







|







291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306

307
308
309
310
311
312
313
314
315
316
317
318
319

320
321

322
323
324
325
326
327
328
329
330
...
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
  }else{
    rc = getStat(0, 0);
  }
  return rc ? 0 : (S_ISDIR(fileStat.st_mode) ? 1 : 2);
}

/*
** Same as file_isdir(), but takes into account symlinks.  Return 1 if
** zFilename is a directory -OR- a symlink that points to a directory.
** Return 0 if zFilename does not exist.  Return 2 if zFilename exists
** but is something other than a directory.
*/
int file_wd_isdir(const char *zFilename){
  int rc;
  char *zFN;


  zFN = mprintf("%s", zFilename);
  file_simplify_name(zFN, -1, 0);
  rc = getStat(zFN, 1);
  if( rc ){
    rc = 0; /* It does not exist at all. */
  }else if( S_ISDIR(fileStat.st_mode) ){
    rc = 1; /* It exists and is a real directory. */
  }else if( S_ISLNK(fileStat.st_mode) ){
    Blob content;
    blob_read_link(&content, zFN); /* It exists and is a link. */
    rc = file_wd_isdir(blob_str(&content)); /* Points to directory? */
    blob_reset(&content);
  }else{

    rc = 2; /* It exists and is something else. */
  }

  free(zFN);
  return rc;
}


/*
** Wrapper around the access() system call.
*/
int file_access(const char *zFilename, int flags){
................................................................................
      /*
      ** On Windows, local path looks like: C:/develop/project/file.txt
      ** The if stops us from trying to create a directory of a drive letter
      ** C: in this example.
      */
      if( !(i==2 && zName[1]==':') ){
#endif
        if( file_mkdir(zName, forceFlag) && file_wd_isdir(zName)!=1 ){
          if (errorReturn <= 0) {
            fossil_fatal_recursive("unable to create directory %s", zName);
          }
          rc = errorReturn;
          break;
        }
#if defined(_WIN32) || defined(__CYGWIN__)

Added test/symlinks.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
#
# Copyright (c) 2016 D. Richard Hipp
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the Simplified BSD License (also
# known as the "2-Clause License" or "FreeBSD License".)
#
# This program is distributed in the hope that it will be useful,
# but without any warranty; without even the implied warranty of
# merchantability or fitness for a particular purpose.
#
# Author contact information:
#   drh@hwaci.com
#   http://www.hwaci.com/drh/
#
############################################################################
#
# Symbolic link tests.
#

if {$tcl_platform(platform) eq "windows"} {
  puts "Symlinks are not supported on Windows."
  test_cleanup_then_return
}

fossil test-th-eval --open-config "setting allow-symlinks"

if {![string is true -strict [normalize_result]]} {
  puts "Symlinks are not enabled."
  test_cleanup_then_return
}

require_no_open_checkout

###############################################################################

test_setup; set rootDir [file normalize [pwd]]

fossil test-th-eval --open-config {repository}
set repository [normalize_result]

if {[string length $repository] == 0} {
  puts "Detection of the open repository file failed."
  test_cleanup_then_return
}

#######################################
# Use symbolic link to a directory... #
#######################################

file mkdir [file join $rootDir subdirA]
exec ln -s [file join $rootDir subdirA] symdirA

###############################################################################

write_file [file join $rootDir subdirA f1.txt] "f1"
write_file [file join $rootDir subdirA f2.txt] "f2"

test symlinks-dir-1 {[file exists [file join $rootDir subdirA f1.txt]] eq 1}
test symlinks-dir-2 {[file exists [file join $rootDir symdirA f1.txt]] eq 1}
test symlinks-dir-3 {[file exists [file join $rootDir subdirA f2.txt]] eq 1}
test symlinks-dir-4 {[file exists [file join $rootDir symdirA f2.txt]] eq 1}

fossil add [file join $rootDir symdirA f1.txt]
fossil commit -m "c1"

###############################################################################

fossil ls
test symlinks-dir-5 {[normalize_result] eq "symdirA/f1.txt"}

###############################################################################

fossil extras
test symlinks-dir-6 {[normalize_result] eq \
"subdirA/f1.txt\nsubdirA/f2.txt\nsymdirA/f2.txt"}

###############################################################################

fossil close
file delete [file join $rootDir subdirA f1.txt]

test symlinks-dir-7 {[file exists [file join $rootDir subdirA f1.txt]] eq 0}
test symlinks-dir-8 {[file exists [file join $rootDir symdirA f1.txt]] eq 0}
test symlinks-dir-9 {[file exists [file join $rootDir subdirA f2.txt]] eq 1}
test symlinks-dir-10 {[file exists [file join $rootDir symdirA f2.txt]] eq 1}

###############################################################################

fossil open $repository
set code [catch {file readlink [file join $rootDir symdirA]} result]

test symlinks-dir-11 {$code == 0}
test symlinks-dir-12 {$result eq [file join $rootDir subdirA]}
test symlinks-dir-13 {[file exists [file join $rootDir subdirA f1.txt]] eq 1}
test symlinks-dir-14 {[file exists [file join $rootDir symdirA f1.txt]] eq 1}
test symlinks-dir-15 {[file exists [file join $rootDir subdirA f2.txt]] eq 1}
test symlinks-dir-16 {[file exists [file join $rootDir symdirA f2.txt]] eq 1}

###############################################################################
#
# TODO: Add tests for symbolic links as files here, including tests with the
#       "allow-symlinks" setting on and off.
#
###############################################################################

test_cleanup