| | @@ -85,15 +85,20 @@ |
| 85 | 85 | /* |
| 86 | 86 | ** Fill stat buf with information received from stat() or lstat(). |
| 87 | 87 | ** lstat() is called on Unix if isWd is TRUE and allow-symlinks setting is on. |
| 88 | 88 | ** |
| 89 | 89 | */ |
| 90 | | -static int fossil_stat(const char *zFilename, struct fossilStat *buf, int isWd){ |
| 90 | +static int fossil_stat( |
| 91 | + const char *zFilename, /* name of file or directory to inspect. */ |
| 92 | + struct fossilStat *buf, /* pointer to buffer where info should go. */ |
| 93 | + int isWd, /* non-zero to consider look at symlink itself. */ |
| 94 | + int forceWd /* non-zero to force look at symlink itself. */ |
| 95 | +){ |
| 91 | 96 | int rc; |
| 92 | 97 | void *zMbcs = fossil_utf8_to_path(zFilename, 0); |
| 93 | 98 | #if !defined(_WIN32) |
| 94 | | - if( isWd && db_allow_symlinks() ){ |
| 99 | + if( isWd && (forceWd || db_allow_symlinks(0)) ){ |
| 95 | 100 | rc = lstat(zMbcs, buf); |
| 96 | 101 | }else{ |
| 97 | 102 | rc = stat(zMbcs, buf); |
| 98 | 103 | } |
| 99 | 104 | #else |
| | @@ -100,10 +105,18 @@ |
| 100 | 105 | rc = win32_stat(zMbcs, buf, isWd); |
| 101 | 106 | #endif |
| 102 | 107 | fossil_path_free(zMbcs); |
| 103 | 108 | return rc; |
| 104 | 109 | } |
| 110 | + |
| 111 | +/* |
| 112 | +** Clears the fileStat variable and its associated validity flag. |
| 113 | +*/ |
| 114 | +static void resetStat(){ |
| 115 | + fileStatValid = 0; |
| 116 | + memset(&fileStat, 0, sizeof(struct fossilStat)); |
| 117 | +} |
| 105 | 118 | |
| 106 | 119 | /* |
| 107 | 120 | ** Fill in the fileStat variable for the file named zFilename. |
| 108 | 121 | ** If zFilename==0, then use the previous value of fileStat if |
| 109 | 122 | ** there is a previous value. |
| | @@ -115,11 +128,11 @@ |
| 115 | 128 | static int getStat(const char *zFilename, int isWd){ |
| 116 | 129 | int rc = 0; |
| 117 | 130 | if( zFilename==0 ){ |
| 118 | 131 | if( fileStatValid==0 ) rc = 1; |
| 119 | 132 | }else{ |
| 120 | | - if( fossil_stat(zFilename, &fileStat, isWd)!=0 ){ |
| 133 | + if( fossil_stat(zFilename, &fileStat, isWd, 0)!=0 ){ |
| 121 | 134 | fileStatValid = 0; |
| 122 | 135 | rc = 1; |
| 123 | 136 | }else{ |
| 124 | 137 | fileStatValid = 1; |
| 125 | 138 | rc = 0; |
| | @@ -157,10 +170,26 @@ |
| 157 | 170 | ** Same as file_mtime(), but takes into account symlinks. |
| 158 | 171 | */ |
| 159 | 172 | i64 file_wd_mtime(const char *zFilename){ |
| 160 | 173 | return getStat(zFilename, 1) ? -1 : fileStat.st_mtime; |
| 161 | 174 | } |
| 175 | + |
| 176 | +/* |
| 177 | +** Return the mode bits for a file. Return -1 if the file does not |
| 178 | +** exist. If zFilename is NULL return the size of the most recently |
| 179 | +** stat-ed file. |
| 180 | +*/ |
| 181 | +int file_mode(const char *zFilename){ |
| 182 | + return getStat(zFilename, 0) ? -1 : fileStat.st_mode; |
| 183 | +} |
| 184 | + |
| 185 | +/* |
| 186 | +** Same as file_mode(), but takes into account symlinks. |
| 187 | +*/ |
| 188 | +int file_wd_mode(const char *zFilename){ |
| 189 | + return getStat(zFilename, 1) ? -1 : fileStat.st_mode; |
| 190 | +} |
| 162 | 191 | |
| 163 | 192 | /* |
| 164 | 193 | ** Return TRUE if the named file is an ordinary file or symlink |
| 165 | 194 | ** and symlinks are allowed. |
| 166 | 195 | ** Return false for directories, devices, fifos, etc. |
| | @@ -191,11 +220,11 @@ |
| 191 | 220 | ** |
| 192 | 221 | ** Arguments: target file (symlink will point to it), link file |
| 193 | 222 | **/ |
| 194 | 223 | void symlink_create(const char *zTargetFile, const char *zLinkFile){ |
| 195 | 224 | #if !defined(_WIN32) |
| 196 | | - if( db_allow_symlinks() ){ |
| 225 | + if( db_allow_symlinks(0) ){ |
| 197 | 226 | int i, nName; |
| 198 | 227 | char *zName, zBuf[1000]; |
| 199 | 228 | |
| 200 | 229 | nName = strlen(zLinkFile); |
| 201 | 230 | if( nName>=sizeof(zBuf) ){ |
| | @@ -248,11 +277,11 @@ |
| 248 | 277 | int file_wd_perm(const char *zFilename){ |
| 249 | 278 | #if !defined(_WIN32) |
| 250 | 279 | if( !getStat(zFilename, 1) ){ |
| 251 | 280 | if( S_ISREG(fileStat.st_mode) && ((S_IXUSR)&fileStat.st_mode)!=0 ) |
| 252 | 281 | return PERM_EXE; |
| 253 | | - else if( db_allow_symlinks() && S_ISLNK(fileStat.st_mode) ) |
| 282 | + else if( db_allow_symlinks(0) && S_ISLNK(fileStat.st_mode) ) |
| 254 | 283 | return PERM_LNK; |
| 255 | 284 | } |
| 256 | 285 | #endif |
| 257 | 286 | return PERM_REG; |
| 258 | 287 | } |
| | @@ -301,19 +330,21 @@ |
| 301 | 330 | ** but is something other than a directory. |
| 302 | 331 | */ |
| 303 | 332 | int file_wd_isdir(const char *zFilename){ |
| 304 | 333 | int rc; |
| 305 | 334 | char *zFN; |
| 335 | + struct fossilStat dirFileStat; |
| 306 | 336 | |
| 307 | 337 | zFN = mprintf("%s", zFilename); |
| 308 | 338 | file_simplify_name(zFN, -1, 0); |
| 309 | | - rc = getStat(zFN, 1); |
| 339 | + memset(&dirFileStat, 0, sizeof(struct fossilStat)); |
| 340 | + rc = fossil_stat(zFN, &dirFileStat, 1, 1); |
| 310 | 341 | if( rc ){ |
| 311 | 342 | rc = 0; /* It does not exist at all. */ |
| 312 | | - }else if( S_ISDIR(fileStat.st_mode) ){ |
| 343 | + }else if( S_ISDIR(dirFileStat.st_mode) ){ |
| 313 | 344 | rc = 1; /* It exists and is a real directory. */ |
| 314 | | - }else if( !g.fNoDirSymlinks && S_ISLNK(fileStat.st_mode) ){ |
| 345 | + }else if( !db_allow_symlinks(1) && S_ISLNK(dirFileStat.st_mode) ){ |
| 315 | 346 | Blob content; |
| 316 | 347 | blob_read_link(&content, zFN); /* It exists and is a link. */ |
| 317 | 348 | rc = file_wd_isdir(blob_str(&content)); /* Points to directory? */ |
| 318 | 349 | blob_reset(&content); |
| 319 | 350 | }else{ |
| | @@ -480,11 +511,11 @@ |
| 480 | 511 | */ |
| 481 | 512 | int file_wd_setexe(const char *zFilename, int onoff){ |
| 482 | 513 | int rc = 0; |
| 483 | 514 | #if !defined(_WIN32) |
| 484 | 515 | struct stat buf; |
| 485 | | - if( fossil_stat(zFilename, &buf, 1)!=0 || S_ISLNK(buf.st_mode) ) return 0; |
| 516 | + if( fossil_stat(zFilename, &buf, 1, 0)!=0 || S_ISLNK(buf.st_mode) ) return 0; |
| 486 | 517 | if( onoff ){ |
| 487 | 518 | int targetMode = (buf.st_mode & 0444)>>2; |
| 488 | 519 | if( (buf.st_mode & 0100)==0 ){ |
| 489 | 520 | chmod(zFilename, buf.st_mode | targetMode); |
| 490 | 521 | rc = 1; |
| | @@ -932,39 +963,123 @@ |
| 932 | 963 | } |
| 933 | 964 | #endif |
| 934 | 965 | blob_resize(pOut, file_simplify_name(blob_buffer(pOut), |
| 935 | 966 | blob_size(pOut), slash)); |
| 936 | 967 | } |
| 968 | + |
| 969 | +/* |
| 970 | +** Emits the effective or raw stat() information for the specified |
| 971 | +** file or directory, optionally preserving the trailing slash and |
| 972 | +** resetting the cached stat() information. |
| 973 | +*/ |
| 974 | +static void emitFileStat( |
| 975 | + const char *zPath, |
| 976 | + int raw, |
| 977 | + int slash, |
| 978 | + int reset |
| 979 | +){ |
| 980 | + char zBuf[100]; |
| 981 | + Blob x; |
| 982 | + memset(zBuf, 0, sizeof(zBuf)); |
| 983 | + blob_zero(&x); |
| 984 | + file_canonical_name(zPath, &x, slash); |
| 985 | + fossil_print("%s[%s] -> [%s]\n", raw ? "RAW " : "", zPath, blob_buffer(&x)); |
| 986 | + blob_reset(&x); |
| 987 | + if( raw ){ |
| 988 | + int rc; |
| 989 | + struct fossilStat testFileStat; |
| 990 | + memset(&testFileStat, 0, sizeof(struct fossilStat)); |
| 991 | + rc = fossil_stat(zPath, &testFileStat, 0, 0); |
| 992 | + fossil_print(" stat_rc = %d\n", rc); |
| 993 | + sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", testFileStat.st_size); |
| 994 | + fossil_print(" stat_size = %s\n", zBuf); |
| 995 | + sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", testFileStat.st_mtime); |
| 996 | + fossil_print(" stat_mtime = %s\n", zBuf); |
| 997 | + fossil_print(" stat_mode = %d\n", testFileStat.st_mode); |
| 998 | + memset(&testFileStat, 0, sizeof(struct fossilStat)); |
| 999 | + rc = fossil_stat(zPath, &testFileStat, 1, 1); |
| 1000 | + fossil_print(" l_stat_rc = %d\n", rc); |
| 1001 | + sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", testFileStat.st_size); |
| 1002 | + fossil_print(" l_stat_size = %s\n", zBuf); |
| 1003 | + sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", testFileStat.st_mtime); |
| 1004 | + fossil_print(" l_stat_mtime = %s\n", zBuf); |
| 1005 | + fossil_print(" l_stat_mode = %d\n", testFileStat.st_mode); |
| 1006 | + }else{ |
| 1007 | + if( reset ) resetStat(); |
| 1008 | + sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", file_wd_size(zPath)); |
| 1009 | + fossil_print(" file_size = %s\n", zBuf); |
| 1010 | + sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", file_wd_mtime(zPath)); |
| 1011 | + fossil_print(" file_mtime = %s\n", zBuf); |
| 1012 | + fossil_print(" file_mode = %d\n", file_wd_mode(zPath)); |
| 1013 | + fossil_print(" file_isfile = %d\n", file_wd_isfile(zPath)); |
| 1014 | + fossil_print(" file_isfile_or_link = %d\n", file_wd_isfile_or_link(zPath)); |
| 1015 | + fossil_print(" file_islink = %d\n", file_wd_islink(zPath)); |
| 1016 | + fossil_print(" file_isexe = %d\n", file_wd_isexe(zPath)); |
| 1017 | + fossil_print(" file_isdir = %d\n", file_wd_isdir(zPath)); |
| 1018 | + if( reset ) resetStat(); |
| 1019 | + } |
| 1020 | +} |
| 1021 | + |
| 1022 | +/* |
| 1023 | +** COMMAND: test-file-environment |
| 1024 | +** |
| 1025 | +** Usage: %fossil test-file-environment FILENAME... |
| 1026 | +** |
| 1027 | +** Display the effective file handling subsystem "settings" and then |
| 1028 | +** display file system information about the files specified, if any. |
| 1029 | +** |
| 1030 | +** Options: |
| 1031 | +** |
| 1032 | +** --open-config Open the configuration database first. |
| 1033 | +** --slash Trailing slashes, if any, are retained. |
| 1034 | +** --reset Reset cached stat() info for each file. |
| 1035 | +*/ |
| 1036 | +void cmd_test_file_environment(void){ |
| 1037 | + int i; |
| 1038 | + int slashFlag = find_option("slash",0,0)!=0; |
| 1039 | + int resetFlag = find_option("reset",0,0)!=0; |
| 1040 | + if( find_option("open-config", 0, 0)!=0 ){ |
| 1041 | + Th_OpenConfig(1); |
| 1042 | + } |
| 1043 | + fossil_print("Th_IsLocalOpen() = %d\n", Th_IsLocalOpen()); |
| 1044 | + fossil_print("Th_IsRepositoryOpen() = %d\n", Th_IsRepositoryOpen()); |
| 1045 | + fossil_print("Th_IsConfigOpen() = %d\n", Th_IsConfigOpen()); |
| 1046 | + fossil_print("filenames_are_case_sensitive() = %d\n", |
| 1047 | + filenames_are_case_sensitive()); |
| 1048 | + fossil_print("db_allow_symlinks_by_default() = %d\n", |
| 1049 | + db_allow_symlinks_by_default()); |
| 1050 | + fossil_print("db_allow_symlinks(0) = %d\n", db_allow_symlinks(0)); |
| 1051 | + fossil_print("db_allow_symlinks(1) = %d\n", db_allow_symlinks(1)); |
| 1052 | + for(i=2; i<g.argc; i++){ |
| 1053 | + emitFileStat(g.argv[i], 1, slashFlag, resetFlag); |
| 1054 | + emitFileStat(g.argv[i], 0, slashFlag, resetFlag); |
| 1055 | + } |
| 1056 | +} |
| 937 | 1057 | |
| 938 | 1058 | /* |
| 939 | 1059 | ** COMMAND: test-canonical-name |
| 940 | 1060 | ** |
| 941 | 1061 | ** Usage: %fossil test-canonical-name FILENAME... |
| 942 | 1062 | ** |
| 943 | 1063 | ** Test the operation of the canonical name generator. |
| 944 | 1064 | ** Also test Fossil's ability to measure attributes of a file. |
| 1065 | +** |
| 1066 | +** Options: |
| 1067 | +** |
| 1068 | +** --open-config Open the configuration database first. |
| 1069 | +** --slash Trailing slashes, if any, are retained. |
| 1070 | +** --reset Reset cached stat() info for each file. |
| 945 | 1071 | */ |
| 946 | 1072 | void cmd_test_canonical_name(void){ |
| 947 | 1073 | int i; |
| 948 | | - Blob x; |
| 949 | 1074 | int slashFlag = find_option("slash",0,0)!=0; |
| 950 | | - blob_zero(&x); |
| 1075 | + int resetFlag = find_option("reset",0,0)!=0; |
| 1076 | + if( find_option("open-config", 0, 0)!=0 ){ |
| 1077 | + Th_OpenConfig(1); |
| 1078 | + } |
| 951 | 1079 | for(i=2; i<g.argc; i++){ |
| 952 | | - char zBuf[100]; |
| 953 | | - const char *zName = g.argv[i]; |
| 954 | | - file_canonical_name(zName, &x, slashFlag); |
| 955 | | - fossil_print("[%s] -> [%s]\n", zName, blob_buffer(&x)); |
| 956 | | - blob_reset(&x); |
| 957 | | - sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", file_wd_size(zName)); |
| 958 | | - fossil_print(" file_size = %s\n", zBuf); |
| 959 | | - sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", file_wd_mtime(zName)); |
| 960 | | - fossil_print(" file_mtime = %s\n", zBuf); |
| 961 | | - fossil_print(" file_isfile = %d\n", file_wd_isfile(zName)); |
| 962 | | - fossil_print(" file_isfile_or_link = %d\n",file_wd_isfile_or_link(zName)); |
| 963 | | - fossil_print(" file_islink = %d\n", file_wd_islink(zName)); |
| 964 | | - fossil_print(" file_isexe = %d\n", file_wd_isexe(zName)); |
| 965 | | - fossil_print(" file_isdir = %d\n", file_wd_isdir(zName)); |
| 1080 | + emitFileStat(g.argv[i], 0, slashFlag, resetFlag); |
| 966 | 1081 | } |
| 967 | 1082 | } |
| 968 | 1083 | |
| 969 | 1084 | /* |
| 970 | 1085 | ** Return TRUE if the given filename is canonical. |
| | @@ -1075,10 +1190,14 @@ |
| 1075 | 1190 | |
| 1076 | 1191 | /* |
| 1077 | 1192 | ** COMMAND: test-relative-name |
| 1078 | 1193 | ** |
| 1079 | 1194 | ** Test the operation of the relative name generator. |
| 1195 | +** |
| 1196 | +** Options: |
| 1197 | +** |
| 1198 | +** --slash Trailing slashes, if any, are retained. |
| 1080 | 1199 | */ |
| 1081 | 1200 | void cmd_test_relative_name(void){ |
| 1082 | 1201 | int i; |
| 1083 | 1202 | Blob x; |
| 1084 | 1203 | int slashFlag = find_option("slash",0,0)!=0; |
| 1085 | 1204 | |