Index: src/checkin.c ================================================================== --- src/checkin.c +++ src/checkin.c @@ -855,12 +855,10 @@ if( zIgnoreFlag==0 ){ zIgnoreFlag = db_get("ignore-glob", 0); } pIgnore = glob_create(zIgnoreFlag); - /* Always consider symlinks. */ - g.allowSymlinks = db_allow_symlinks_by_default(); locate_unmanaged_files(g.argc-2, g.argv+2, scanFlags, pIgnore); glob_free(pIgnore); blob_zero(&report); status_report(&report, flags); @@ -902,13 +900,14 @@ ** ** The default values for --clean, --ignore, and --keep are determined by ** the (versionable) clean-glob, ignore-glob, and keep-glob settings. ** ** The --verily option ignores the keep-glob and ignore-glob settings and -** turns on --force, --emptydirs, --dotfiles, and --disable-undo. Use the -** --verily option when you really want to clean up everything. Extreme -** care should be exercised when using the --verily option. +** turns on the options --force, --emptydirs, --dotfiles, --disable-undo, +** and --no-dir-symlinks. Use the --verily option when you really want +** to clean up everything. Extreme care should be exercised when using +** the --verily option. ** ** Options: ** --allckouts Check for empty directories within any checkouts ** that may be nested within the current one. This ** option should be used with great care because the @@ -999,11 +998,12 @@ verilyFlag = allFileFlag = allDirFlag = 1; emptyDirsFlag = 1; disableUndo = 1; scanFlags |= SCAN_ALL; zCleanFlag = 0; - g.fNoDirSymlinks = 1; + g.fNoDirSymlinks = 1; /* Forbid symlink directory traversal. */ + g.allowSymlinks = 1; /* Treat symlink files as content. */ } if( zIgnoreFlag==0 && !verilyFlag ){ zIgnoreFlag = db_get("ignore-glob", 0); } if( zKeepFlag==0 && !verilyFlag ){ @@ -1016,12 +1016,10 @@ verify_all_options(); pIgnore = glob_create(zIgnoreFlag); pKeep = glob_create(zKeepFlag); pClean = glob_create(zCleanFlag); nRoot = (int)strlen(g.zLocalRoot); - /* Always consider symlinks. */ - g.allowSymlinks = db_allow_symlinks_by_default(); if( !dirsOnlyFlag ){ Stmt q; Blob repo; if( !dryRunFlag && !disableUndo ) undo_begin(); locate_unmanaged_files(g.argc-2, g.argv+2, scanFlags, pIgnore); Index: src/db.c ================================================================== --- src/db.c +++ src/db.c @@ -1436,12 +1436,17 @@ } /* ** Returns non-zero if support for symlinks is currently enabled. */ -int db_allow_symlinks(void){ - return g.allowSymlinks; +int db_allow_symlinks(int traversal){ + if( traversal ){ + if( g.allowSymlinks ) return 1; + return g.fNoDirSymlinks; + }else{ + return g.allowSymlinks; + } } /* ** Open the repository database given by zDbName. If zDbName==NULL then ** get the name from the already open local database. Index: src/file.c ================================================================== --- src/file.c +++ src/file.c @@ -85,15 +85,20 @@ /* ** Fill stat buf with information received from stat() or lstat(). ** lstat() is called on Unix if isWd is TRUE and allow-symlinks setting is on. ** */ -static int fossil_stat(const char *zFilename, struct fossilStat *buf, int isWd){ +static int fossil_stat( + const char *zFilename, /* name of file or directory to inspect. */ + struct fossilStat *buf, /* pointer to buffer where info should go. */ + int isWd, /* non-zero to consider look at symlink itself. */ + int forceWd /* non-zero to force look at symlink itself. */ +){ int rc; void *zMbcs = fossil_utf8_to_path(zFilename, 0); #if !defined(_WIN32) - if( isWd && db_allow_symlinks() ){ + if( isWd && (forceWd || db_allow_symlinks(0)) ){ rc = lstat(zMbcs, buf); }else{ rc = stat(zMbcs, buf); } #else @@ -100,10 +105,18 @@ rc = win32_stat(zMbcs, buf, isWd); #endif fossil_path_free(zMbcs); return rc; } + +/* +** Clears the fileStat variable and its associated validity flag. +*/ +static void resetStat(){ + fileStatValid = 0; + memset(&fileStat, 0, sizeof(struct fossilStat)); +} /* ** Fill in the fileStat variable for the file named zFilename. ** If zFilename==0, then use the previous value of fileStat if ** there is a previous value. @@ -115,11 +128,11 @@ static int getStat(const char *zFilename, int isWd){ int rc = 0; if( zFilename==0 ){ if( fileStatValid==0 ) rc = 1; }else{ - if( fossil_stat(zFilename, &fileStat, isWd)!=0 ){ + if( fossil_stat(zFilename, &fileStat, isWd, 0)!=0 ){ fileStatValid = 0; rc = 1; }else{ fileStatValid = 1; rc = 0; @@ -157,10 +170,26 @@ ** Same as file_mtime(), but takes into account symlinks. */ i64 file_wd_mtime(const char *zFilename){ return getStat(zFilename, 1) ? -1 : fileStat.st_mtime; } + +/* +** Return the mode bits for a file. Return -1 if the file does not +** exist. If zFilename is NULL return the size of the most recently +** stat-ed file. +*/ +int file_mode(const char *zFilename){ + return getStat(zFilename, 0) ? -1 : fileStat.st_mode; +} + +/* +** Same as file_mode(), but takes into account symlinks. +*/ +int file_wd_mode(const char *zFilename){ + return getStat(zFilename, 1) ? -1 : fileStat.st_mode; +} /* ** Return TRUE if the named file is an ordinary file or symlink ** and symlinks are allowed. ** Return false for directories, devices, fifos, etc. @@ -191,11 +220,11 @@ ** ** Arguments: target file (symlink will point to it), link file **/ void symlink_create(const char *zTargetFile, const char *zLinkFile){ #if !defined(_WIN32) - if( db_allow_symlinks() ){ + if( db_allow_symlinks(0) ){ int i, nName; char *zName, zBuf[1000]; nName = strlen(zLinkFile); if( nName>=sizeof(zBuf) ){ @@ -248,11 +277,11 @@ int file_wd_perm(const char *zFilename){ #if !defined(_WIN32) if( !getStat(zFilename, 1) ){ if( S_ISREG(fileStat.st_mode) && ((S_IXUSR)&fileStat.st_mode)!=0 ) return PERM_EXE; - else if( db_allow_symlinks() && S_ISLNK(fileStat.st_mode) ) + else if( db_allow_symlinks(0) && S_ISLNK(fileStat.st_mode) ) return PERM_LNK; } #endif return PERM_REG; } @@ -301,19 +330,21 @@ ** but is something other than a directory. */ int file_wd_isdir(const char *zFilename){ int rc; char *zFN; + struct fossilStat dirFileStat; zFN = mprintf("%s", zFilename); file_simplify_name(zFN, -1, 0); - rc = getStat(zFN, 1); + memset(&dirFileStat, 0, sizeof(struct fossilStat)); + rc = fossil_stat(zFN, &dirFileStat, 1, 1); if( rc ){ rc = 0; /* It does not exist at all. */ - }else if( S_ISDIR(fileStat.st_mode) ){ + }else if( S_ISDIR(dirFileStat.st_mode) ){ rc = 1; /* It exists and is a real directory. */ - }else if( !g.fNoDirSymlinks && S_ISLNK(fileStat.st_mode) ){ + }else if( !db_allow_symlinks(1) && S_ISLNK(dirFileStat.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{ @@ -480,11 +511,11 @@ */ int file_wd_setexe(const char *zFilename, int onoff){ int rc = 0; #if !defined(_WIN32) struct stat buf; - if( fossil_stat(zFilename, &buf, 1)!=0 || S_ISLNK(buf.st_mode) ) return 0; + if( fossil_stat(zFilename, &buf, 1, 0)!=0 || S_ISLNK(buf.st_mode) ) return 0; if( onoff ){ int targetMode = (buf.st_mode & 0444)>>2; if( (buf.st_mode & 0100)==0 ){ chmod(zFilename, buf.st_mode | targetMode); rc = 1; @@ -932,39 +963,123 @@ } #endif blob_resize(pOut, file_simplify_name(blob_buffer(pOut), blob_size(pOut), slash)); } + +/* +** Emits the effective or raw stat() information for the specified +** file or directory, optionally preserving the trailing slash and +** resetting the cached stat() information. +*/ +static void emitFileStat( + const char *zPath, + int raw, + int slash, + int reset +){ + char zBuf[100]; + Blob x; + memset(zBuf, 0, sizeof(zBuf)); + blob_zero(&x); + file_canonical_name(zPath, &x, slash); + fossil_print("%s[%s] -> [%s]\n", raw ? "RAW " : "", zPath, blob_buffer(&x)); + blob_reset(&x); + if( raw ){ + int rc; + struct fossilStat testFileStat; + memset(&testFileStat, 0, sizeof(struct fossilStat)); + rc = fossil_stat(zPath, &testFileStat, 0, 0); + fossil_print(" stat_rc = %d\n", rc); + sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", testFileStat.st_size); + fossil_print(" stat_size = %s\n", zBuf); + sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", testFileStat.st_mtime); + fossil_print(" stat_mtime = %s\n", zBuf); + fossil_print(" stat_mode = %d\n", testFileStat.st_mode); + memset(&testFileStat, 0, sizeof(struct fossilStat)); + rc = fossil_stat(zPath, &testFileStat, 1, 1); + fossil_print(" l_stat_rc = %d\n", rc); + sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", testFileStat.st_size); + fossil_print(" l_stat_size = %s\n", zBuf); + sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", testFileStat.st_mtime); + fossil_print(" l_stat_mtime = %s\n", zBuf); + fossil_print(" l_stat_mode = %d\n", testFileStat.st_mode); + }else{ + if( reset ) resetStat(); + sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", file_wd_size(zPath)); + fossil_print(" file_size = %s\n", zBuf); + sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", file_wd_mtime(zPath)); + fossil_print(" file_mtime = %s\n", zBuf); + fossil_print(" file_mode = %d\n", file_wd_mode(zPath)); + fossil_print(" file_isfile = %d\n", file_wd_isfile(zPath)); + fossil_print(" file_isfile_or_link = %d\n", file_wd_isfile_or_link(zPath)); + fossil_print(" file_islink = %d\n", file_wd_islink(zPath)); + fossil_print(" file_isexe = %d\n", file_wd_isexe(zPath)); + fossil_print(" file_isdir = %d\n", file_wd_isdir(zPath)); + if( reset ) resetStat(); + } +} + +/* +** COMMAND: test-file-environment +** +** Usage: %fossil test-file-environment FILENAME... +** +** Display the effective file handling subsystem "settings" and then +** display file system information about the files specified, if any. +** +** Options: +** +** --open-config Open the configuration database first. +** --slash Trailing slashes, if any, are retained. +** --reset Reset cached stat() info for each file. +*/ +void cmd_test_file_environment(void){ + int i; + int slashFlag = find_option("slash",0,0)!=0; + int resetFlag = find_option("reset",0,0)!=0; + if( find_option("open-config", 0, 0)!=0 ){ + Th_OpenConfig(1); + } + fossil_print("Th_IsLocalOpen() = %d\n", Th_IsLocalOpen()); + fossil_print("Th_IsRepositoryOpen() = %d\n", Th_IsRepositoryOpen()); + fossil_print("Th_IsConfigOpen() = %d\n", Th_IsConfigOpen()); + fossil_print("filenames_are_case_sensitive() = %d\n", + filenames_are_case_sensitive()); + fossil_print("db_allow_symlinks_by_default() = %d\n", + db_allow_symlinks_by_default()); + fossil_print("db_allow_symlinks(0) = %d\n", db_allow_symlinks(0)); + fossil_print("db_allow_symlinks(1) = %d\n", db_allow_symlinks(1)); + for(i=2; i [%s]\n", zName, blob_buffer(&x)); - blob_reset(&x); - sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", file_wd_size(zName)); - fossil_print(" file_size = %s\n", zBuf); - sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", file_wd_mtime(zName)); - fossil_print(" file_mtime = %s\n", zBuf); - fossil_print(" file_isfile = %d\n", file_wd_isfile(zName)); - fossil_print(" file_isfile_or_link = %d\n",file_wd_isfile_or_link(zName)); - fossil_print(" file_islink = %d\n", file_wd_islink(zName)); - fossil_print(" file_isexe = %d\n", file_wd_isexe(zName)); - fossil_print(" file_isdir = %d\n", file_wd_isdir(zName)); + emitFileStat(g.argv[i], 0, slashFlag, resetFlag); } } /* ** Return TRUE if the given filename is canonical. @@ -1075,10 +1190,14 @@ /* ** COMMAND: test-relative-name ** ** Test the operation of the relative name generator. +** +** Options: +** +** --slash Trailing slashes, if any, are retained. */ void cmd_test_relative_name(void){ int i; Blob x; int slashFlag = find_option("slash",0,0)!=0; Index: src/th_main.c ================================================================== --- src/th_main.c +++ src/th_main.c @@ -21,10 +21,18 @@ #include "config.h" #include "th_main.h" #include "sqlite3.h" #if INTERFACE +/* +** These macros are used within this file to detect if the repository and +** configuration ("user") database are currently open. +*/ +#define Th_IsLocalOpen() (g.localOpen) +#define Th_IsRepositoryOpen() (g.repositoryOpen) +#define Th_IsConfigOpen() (g.zConfigDbName!=0) + /* ** Flag parameters to the Th_FossilInit() routine used to control the ** interpreter creation and initialization process. */ #define TH_INIT_NONE ((u32)0x00000000) /* No flags. */ @@ -59,17 +67,10 @@ */ #define NO_COMMAND_HOOK_ERROR "no such command: command_hook" #define NO_WEBPAGE_HOOK_ERROR "no such command: webpage_hook" #endif -/* -** These macros are used within this file to detect if the repository and -** configuration ("user") database are currently open. -*/ -#define Th_IsRepositoryOpen() (g.repositoryOpen) -#define Th_IsConfigOpen() (g.zConfigDbName!=0) - /* ** Global variable counting the number of outstanding calls to malloc() ** made by the th1 implementation. This is used to catch memory leaks ** in the interpreter. Obviously, it also means th1 is not threadsafe. */