Fossil SCM
Improvements to the Security-Audit page - mostly in providing additional information about the files in the CGI extension folder.
Commit
a3bc6552eea4676d3bb55ed5b40ab608570e30b618d9edc3f2f6ce6ae4a77187
Parent
534b060007692cc…
8 files changed
+2
-2
+3
-3
+92
-11
+2
+1
-1
+15
-8
+5
-1
+43
-22
+2
-2
| --- src/add.c | ||
| +++ src/add.c | ||
| @@ -328,11 +328,11 @@ | ||
| 328 | 328 | |
| 329 | 329 | file_canonical_name(g.argv[i], &fullName, 0); |
| 330 | 330 | zName = blob_str(&fullName); |
| 331 | 331 | isDir = file_isdir(zName, RepoFILE); |
| 332 | 332 | if( isDir==1 ){ |
| 333 | - vfile_scan(&fullName, nRoot-1, scanFlags, pClean, pIgnore); | |
| 333 | + vfile_scan(&fullName, nRoot-1, scanFlags, pClean, pIgnore, RepoFILE); | |
| 334 | 334 | }else if( isDir==0 ){ |
| 335 | 335 | fossil_warning("not found: %s", zName); |
| 336 | 336 | }else{ |
| 337 | 337 | char *zTreeName = &zName[nRoot]; |
| 338 | 338 | if( !forceFlag && glob_match(pIgnore, zTreeName) ){ |
| @@ -677,11 +677,11 @@ | ||
| 677 | 677 | n = strlen(g.zLocalRoot); |
| 678 | 678 | blob_init(&path, g.zLocalRoot, n-1); |
| 679 | 679 | /* now we read the complete file structure into a temp table */ |
| 680 | 680 | pClean = glob_create(zCleanFlag); |
| 681 | 681 | pIgnore = glob_create(zIgnoreFlag); |
| 682 | - vfile_scan(&path, blob_size(&path), scanFlags, pClean, pIgnore); | |
| 682 | + vfile_scan(&path, blob_size(&path), scanFlags, pClean, pIgnore, RepoFILE); | |
| 683 | 683 | glob_free(pIgnore); |
| 684 | 684 | glob_free(pClean); |
| 685 | 685 | nAdd = add_files_in_sfile(vid); |
| 686 | 686 | |
| 687 | 687 | /* step 2: search for missing files */ |
| 688 | 688 |
| --- src/add.c | |
| +++ src/add.c | |
| @@ -328,11 +328,11 @@ | |
| 328 | |
| 329 | file_canonical_name(g.argv[i], &fullName, 0); |
| 330 | zName = blob_str(&fullName); |
| 331 | isDir = file_isdir(zName, RepoFILE); |
| 332 | if( isDir==1 ){ |
| 333 | vfile_scan(&fullName, nRoot-1, scanFlags, pClean, pIgnore); |
| 334 | }else if( isDir==0 ){ |
| 335 | fossil_warning("not found: %s", zName); |
| 336 | }else{ |
| 337 | char *zTreeName = &zName[nRoot]; |
| 338 | if( !forceFlag && glob_match(pIgnore, zTreeName) ){ |
| @@ -677,11 +677,11 @@ | |
| 677 | n = strlen(g.zLocalRoot); |
| 678 | blob_init(&path, g.zLocalRoot, n-1); |
| 679 | /* now we read the complete file structure into a temp table */ |
| 680 | pClean = glob_create(zCleanFlag); |
| 681 | pIgnore = glob_create(zIgnoreFlag); |
| 682 | vfile_scan(&path, blob_size(&path), scanFlags, pClean, pIgnore); |
| 683 | glob_free(pIgnore); |
| 684 | glob_free(pClean); |
| 685 | nAdd = add_files_in_sfile(vid); |
| 686 | |
| 687 | /* step 2: search for missing files */ |
| 688 |
| --- src/add.c | |
| +++ src/add.c | |
| @@ -328,11 +328,11 @@ | |
| 328 | |
| 329 | file_canonical_name(g.argv[i], &fullName, 0); |
| 330 | zName = blob_str(&fullName); |
| 331 | isDir = file_isdir(zName, RepoFILE); |
| 332 | if( isDir==1 ){ |
| 333 | vfile_scan(&fullName, nRoot-1, scanFlags, pClean, pIgnore, RepoFILE); |
| 334 | }else if( isDir==0 ){ |
| 335 | fossil_warning("not found: %s", zName); |
| 336 | }else{ |
| 337 | char *zTreeName = &zName[nRoot]; |
| 338 | if( !forceFlag && glob_match(pIgnore, zTreeName) ){ |
| @@ -677,11 +677,11 @@ | |
| 677 | n = strlen(g.zLocalRoot); |
| 678 | blob_init(&path, g.zLocalRoot, n-1); |
| 679 | /* now we read the complete file structure into a temp table */ |
| 680 | pClean = glob_create(zCleanFlag); |
| 681 | pIgnore = glob_create(zIgnoreFlag); |
| 682 | vfile_scan(&path, blob_size(&path), scanFlags, pClean, pIgnore, RepoFILE); |
| 683 | glob_free(pIgnore); |
| 684 | glob_free(pClean); |
| 685 | nAdd = add_files_in_sfile(vid); |
| 686 | |
| 687 | /* step 2: search for missing files */ |
| 688 |
+3
-3
| --- src/checkin.c | ||
| +++ src/checkin.c | ||
| @@ -80,19 +80,19 @@ | ||
| 80 | 80 | db_multi_exec("CREATE TEMP TABLE sfile(pathname TEXT PRIMARY KEY %s," |
| 81 | 81 | " mtime INTEGER, size INTEGER)", filename_collation()); |
| 82 | 82 | nRoot = (int)strlen(g.zLocalRoot); |
| 83 | 83 | if( argc==0 ){ |
| 84 | 84 | blob_init(&name, g.zLocalRoot, nRoot - 1); |
| 85 | - vfile_scan(&name, blob_size(&name), scanFlags, pIgnore, 0); | |
| 85 | + vfile_scan(&name, blob_size(&name), scanFlags, pIgnore, 0, RepoFILE); | |
| 86 | 86 | blob_reset(&name); |
| 87 | 87 | }else{ |
| 88 | 88 | for(i=0; i<argc; i++){ |
| 89 | 89 | file_canonical_name(argv[i], &name, 0); |
| 90 | 90 | zName = blob_str(&name); |
| 91 | 91 | isDir = file_isdir(zName, RepoFILE); |
| 92 | 92 | if( isDir==1 ){ |
| 93 | - vfile_scan(&name, nRoot-1, scanFlags, pIgnore, 0); | |
| 93 | + vfile_scan(&name, nRoot-1, scanFlags, pIgnore, 0, RepoFILE); | |
| 94 | 94 | }else if( isDir==0 ){ |
| 95 | 95 | fossil_warning("not found: %s", &zName[nRoot]); |
| 96 | 96 | }else if( file_access(zName, R_OK) ){ |
| 97 | 97 | fossil_fatal("cannot open %s", &zName[nRoot]); |
| 98 | 98 | }else{ |
| @@ -1105,11 +1105,11 @@ | ||
| 1105 | 1105 | Glob *pEmptyDirs = glob_create(db_get("empty-dirs", 0)); |
| 1106 | 1106 | Stmt q; |
| 1107 | 1107 | Blob root; |
| 1108 | 1108 | blob_init(&root, g.zLocalRoot, nRoot - 1); |
| 1109 | 1109 | vfile_dir_scan(&root, blob_size(&root), scanFlags, pIgnore, |
| 1110 | - pEmptyDirs); | |
| 1110 | + pEmptyDirs, RepoFILE); | |
| 1111 | 1111 | blob_reset(&root); |
| 1112 | 1112 | db_prepare(&q, |
| 1113 | 1113 | "SELECT %Q || x FROM dscan_temp" |
| 1114 | 1114 | " WHERE x NOT IN (%s) AND y = 0" |
| 1115 | 1115 | " ORDER BY 1 DESC", |
| 1116 | 1116 |
| --- src/checkin.c | |
| +++ src/checkin.c | |
| @@ -80,19 +80,19 @@ | |
| 80 | db_multi_exec("CREATE TEMP TABLE sfile(pathname TEXT PRIMARY KEY %s," |
| 81 | " mtime INTEGER, size INTEGER)", filename_collation()); |
| 82 | nRoot = (int)strlen(g.zLocalRoot); |
| 83 | if( argc==0 ){ |
| 84 | blob_init(&name, g.zLocalRoot, nRoot - 1); |
| 85 | vfile_scan(&name, blob_size(&name), scanFlags, pIgnore, 0); |
| 86 | blob_reset(&name); |
| 87 | }else{ |
| 88 | for(i=0; i<argc; i++){ |
| 89 | file_canonical_name(argv[i], &name, 0); |
| 90 | zName = blob_str(&name); |
| 91 | isDir = file_isdir(zName, RepoFILE); |
| 92 | if( isDir==1 ){ |
| 93 | vfile_scan(&name, nRoot-1, scanFlags, pIgnore, 0); |
| 94 | }else if( isDir==0 ){ |
| 95 | fossil_warning("not found: %s", &zName[nRoot]); |
| 96 | }else if( file_access(zName, R_OK) ){ |
| 97 | fossil_fatal("cannot open %s", &zName[nRoot]); |
| 98 | }else{ |
| @@ -1105,11 +1105,11 @@ | |
| 1105 | Glob *pEmptyDirs = glob_create(db_get("empty-dirs", 0)); |
| 1106 | Stmt q; |
| 1107 | Blob root; |
| 1108 | blob_init(&root, g.zLocalRoot, nRoot - 1); |
| 1109 | vfile_dir_scan(&root, blob_size(&root), scanFlags, pIgnore, |
| 1110 | pEmptyDirs); |
| 1111 | blob_reset(&root); |
| 1112 | db_prepare(&q, |
| 1113 | "SELECT %Q || x FROM dscan_temp" |
| 1114 | " WHERE x NOT IN (%s) AND y = 0" |
| 1115 | " ORDER BY 1 DESC", |
| 1116 |
| --- src/checkin.c | |
| +++ src/checkin.c | |
| @@ -80,19 +80,19 @@ | |
| 80 | db_multi_exec("CREATE TEMP TABLE sfile(pathname TEXT PRIMARY KEY %s," |
| 81 | " mtime INTEGER, size INTEGER)", filename_collation()); |
| 82 | nRoot = (int)strlen(g.zLocalRoot); |
| 83 | if( argc==0 ){ |
| 84 | blob_init(&name, g.zLocalRoot, nRoot - 1); |
| 85 | vfile_scan(&name, blob_size(&name), scanFlags, pIgnore, 0, RepoFILE); |
| 86 | blob_reset(&name); |
| 87 | }else{ |
| 88 | for(i=0; i<argc; i++){ |
| 89 | file_canonical_name(argv[i], &name, 0); |
| 90 | zName = blob_str(&name); |
| 91 | isDir = file_isdir(zName, RepoFILE); |
| 92 | if( isDir==1 ){ |
| 93 | vfile_scan(&name, nRoot-1, scanFlags, pIgnore, 0, RepoFILE); |
| 94 | }else if( isDir==0 ){ |
| 95 | fossil_warning("not found: %s", &zName[nRoot]); |
| 96 | }else if( file_access(zName, R_OK) ){ |
| 97 | fossil_fatal("cannot open %s", &zName[nRoot]); |
| 98 | }else{ |
| @@ -1105,11 +1105,11 @@ | |
| 1105 | Glob *pEmptyDirs = glob_create(db_get("empty-dirs", 0)); |
| 1106 | Stmt q; |
| 1107 | Blob root; |
| 1108 | blob_init(&root, g.zLocalRoot, nRoot - 1); |
| 1109 | vfile_dir_scan(&root, blob_size(&root), scanFlags, pIgnore, |
| 1110 | pEmptyDirs, RepoFILE); |
| 1111 | blob_reset(&root); |
| 1112 | db_prepare(&q, |
| 1113 | "SELECT %Q || x FROM dscan_temp" |
| 1114 | " WHERE x NOT IN (%s) AND y = 0" |
| 1115 | " ORDER BY 1 DESC", |
| 1116 |
+92
-11
| --- src/extcgi.c | ||
| +++ src/extcgi.c | ||
| @@ -74,10 +74,33 @@ | ||
| 74 | 74 | "SCRIPT_NAME", |
| 75 | 75 | "SERVER_NAME", |
| 76 | 76 | "SERVER_PORT", |
| 77 | 77 | "SERVER_PROTOCOL", |
| 78 | 78 | }; |
| 79 | + | |
| 80 | +/* | |
| 81 | +** Check a pathname to determine if it is acceptable for use as | |
| 82 | +** extension CGI. Some pathnames are excluded for security reasons. | |
| 83 | +** Return NULL on success or a static error string if there is | |
| 84 | +** a failure. | |
| 85 | +*/ | |
| 86 | +const char *ext_pathname_ok(const char *zName){ | |
| 87 | + int i; | |
| 88 | + const char *zFailReason = 0; | |
| 89 | + for(i=0; zName[i]; i++){ | |
| 90 | + char c = zName[i]; | |
| 91 | + if( (c=='.' || c=='-') && (i==0 || zName[i-1]=='/') ){ | |
| 92 | + zFailReason = "path element begins with '.' or '-'"; | |
| 93 | + break; | |
| 94 | + } | |
| 95 | + if( !fossil_isalnum(c) && c!='_' && c!='-' && c!='.' && c!='/' ){ | |
| 96 | + zFailReason = "illegal character in path"; | |
| 97 | + break; | |
| 98 | + } | |
| 99 | + } | |
| 100 | + return zFailReason; | |
| 101 | +} | |
| 79 | 102 | |
| 80 | 103 | /* |
| 81 | 104 | ** WEBPAGE: ext raw-content |
| 82 | 105 | ** |
| 83 | 106 | ** Relay an HTTP request to secondary CGI after first checking the |
| @@ -131,21 +154,13 @@ | ||
| 131 | 154 | } |
| 132 | 155 | if( zName==0 ){ |
| 133 | 156 | zFailReason = "no path beyond /ext"; |
| 134 | 157 | goto ext_not_found; |
| 135 | 158 | } |
| 136 | - for(i=0; zName[i]; i++){ | |
| 137 | - char c = zName[i]; | |
| 138 | - if( (c=='.' || c=='-') && (i==0 || zName[i-1]=='/') ){ | |
| 139 | - zFailReason = "path element begins with '.' or '-'"; | |
| 140 | - goto ext_not_found; | |
| 141 | - } | |
| 142 | - if( !fossil_isalnum(c) && c!='_' && c!='-' && c!='.' && c!='/' ){ | |
| 143 | - zFailReason = "illegal character in path"; | |
| 144 | - goto ext_not_found; | |
| 145 | - } | |
| 146 | - } | |
| 159 | + zFailReason = ext_pathname_ok(zName); | |
| 160 | + if( zFailReason ) goto ext_not_found; | |
| 161 | + zFailReason = "???"; | |
| 147 | 162 | if( file_isdir(g.zExtRoot,ExtFILE)!=1 ){ |
| 148 | 163 | zFailReason = "extroot is not a directory"; |
| 149 | 164 | goto ext_not_found; |
| 150 | 165 | } |
| 151 | 166 | zPath = mprintf("%s/%s", g.zExtRoot, zName); |
| @@ -297,5 +312,71 @@ | ||
| 297 | 312 | @ <p>Reason for failure: %h(zFailReason)</p> |
| 298 | 313 | } |
| 299 | 314 | } |
| 300 | 315 | return; |
| 301 | 316 | } |
| 317 | + | |
| 318 | +/* | |
| 319 | +** Create a temporary SFILE table and fill it with one entry for each file | |
| 320 | +** in the extension document root directory (g.zExtRoot). The SFILE table | |
| 321 | +** looks like this: | |
| 322 | +** | |
| 323 | +** CREATE TEMP TABLE sfile( | |
| 324 | +** pathname TEXT PRIMARY KEY, | |
| 325 | +** isexe BOOLEAN | |
| 326 | +** ) WITHOUT ROWID; | |
| 327 | +*/ | |
| 328 | +void ext_files(void){ | |
| 329 | + Blob base; | |
| 330 | + db_multi_exec( | |
| 331 | + "CREATE TEMP TABLE sfile(\n" | |
| 332 | + " pathname TEXT PRIMARY KEY,\n" | |
| 333 | + " isexe BOOLEAN\n" | |
| 334 | + ") WITHOUT ROWID;" | |
| 335 | + ); | |
| 336 | + blob_init(&base, g.zExtRoot, -1); | |
| 337 | + vfile_scan(&base, blob_size(&base), | |
| 338 | + SCAN_ALL|SCAN_ISEXE, | |
| 339 | + 0, 0, ExtFILE); | |
| 340 | + blob_zero(&base); | |
| 341 | +} | |
| 342 | + | |
| 343 | +/* | |
| 344 | +** WEBPAGE: extfilelist | |
| 345 | +** | |
| 346 | +** List all files in the extension CGI document root and its subfolders. | |
| 347 | +*/ | |
| 348 | +void ext_filelist_page(void){ | |
| 349 | + Stmt q; | |
| 350 | + login_check_credentials(); | |
| 351 | + if( !g.perm.Admin ){ | |
| 352 | + login_needed(0); | |
| 353 | + return; | |
| 354 | + } | |
| 355 | + ext_files(); | |
| 356 | + style_header("CGI Extension Filelist"); | |
| 357 | + @ <table border="0" cellspacing="0" cellpadding="3"> | |
| 358 | + @ <tbody> | |
| 359 | + db_prepare(&q, "SELECT pathname, isexe FROM sfile" | |
| 360 | + " ORDER BY pathname"); | |
| 361 | + while( db_step(&q)==SQLITE_ROW ){ | |
| 362 | + const char *zName = db_column_text(&q,0); | |
| 363 | + int isExe = db_column_int(&q,1); | |
| 364 | + @ <tr> | |
| 365 | + if( ext_pathname_ok(zName)!=0 ){ | |
| 366 | + @ <td><span style="opacity:0.5;">%h(zName)</span></td> | |
| 367 | + @ <td>data file</td> | |
| 368 | + }else{ | |
| 369 | + @ <td><a href="%R/ext/%h(zName)">%h(zName)</a></td> | |
| 370 | + if( isExe ){ | |
| 371 | + @ <td>CGI</td> | |
| 372 | + }else{ | |
| 373 | + @ <td>static content</td> | |
| 374 | + } | |
| 375 | + } | |
| 376 | + @ </tr> | |
| 377 | + } | |
| 378 | + db_finalize(&q); | |
| 379 | + @ </tbody> | |
| 380 | + @ </table> | |
| 381 | + style_footer(); | |
| 382 | +} | |
| 302 | 383 |
| --- src/extcgi.c | |
| +++ src/extcgi.c | |
| @@ -74,10 +74,33 @@ | |
| 74 | "SCRIPT_NAME", |
| 75 | "SERVER_NAME", |
| 76 | "SERVER_PORT", |
| 77 | "SERVER_PROTOCOL", |
| 78 | }; |
| 79 | |
| 80 | /* |
| 81 | ** WEBPAGE: ext raw-content |
| 82 | ** |
| 83 | ** Relay an HTTP request to secondary CGI after first checking the |
| @@ -131,21 +154,13 @@ | |
| 131 | } |
| 132 | if( zName==0 ){ |
| 133 | zFailReason = "no path beyond /ext"; |
| 134 | goto ext_not_found; |
| 135 | } |
| 136 | for(i=0; zName[i]; i++){ |
| 137 | char c = zName[i]; |
| 138 | if( (c=='.' || c=='-') && (i==0 || zName[i-1]=='/') ){ |
| 139 | zFailReason = "path element begins with '.' or '-'"; |
| 140 | goto ext_not_found; |
| 141 | } |
| 142 | if( !fossil_isalnum(c) && c!='_' && c!='-' && c!='.' && c!='/' ){ |
| 143 | zFailReason = "illegal character in path"; |
| 144 | goto ext_not_found; |
| 145 | } |
| 146 | } |
| 147 | if( file_isdir(g.zExtRoot,ExtFILE)!=1 ){ |
| 148 | zFailReason = "extroot is not a directory"; |
| 149 | goto ext_not_found; |
| 150 | } |
| 151 | zPath = mprintf("%s/%s", g.zExtRoot, zName); |
| @@ -297,5 +312,71 @@ | |
| 297 | @ <p>Reason for failure: %h(zFailReason)</p> |
| 298 | } |
| 299 | } |
| 300 | return; |
| 301 | } |
| 302 |
| --- src/extcgi.c | |
| +++ src/extcgi.c | |
| @@ -74,10 +74,33 @@ | |
| 74 | "SCRIPT_NAME", |
| 75 | "SERVER_NAME", |
| 76 | "SERVER_PORT", |
| 77 | "SERVER_PROTOCOL", |
| 78 | }; |
| 79 | |
| 80 | /* |
| 81 | ** Check a pathname to determine if it is acceptable for use as |
| 82 | ** extension CGI. Some pathnames are excluded for security reasons. |
| 83 | ** Return NULL on success or a static error string if there is |
| 84 | ** a failure. |
| 85 | */ |
| 86 | const char *ext_pathname_ok(const char *zName){ |
| 87 | int i; |
| 88 | const char *zFailReason = 0; |
| 89 | for(i=0; zName[i]; i++){ |
| 90 | char c = zName[i]; |
| 91 | if( (c=='.' || c=='-') && (i==0 || zName[i-1]=='/') ){ |
| 92 | zFailReason = "path element begins with '.' or '-'"; |
| 93 | break; |
| 94 | } |
| 95 | if( !fossil_isalnum(c) && c!='_' && c!='-' && c!='.' && c!='/' ){ |
| 96 | zFailReason = "illegal character in path"; |
| 97 | break; |
| 98 | } |
| 99 | } |
| 100 | return zFailReason; |
| 101 | } |
| 102 | |
| 103 | /* |
| 104 | ** WEBPAGE: ext raw-content |
| 105 | ** |
| 106 | ** Relay an HTTP request to secondary CGI after first checking the |
| @@ -131,21 +154,13 @@ | |
| 154 | } |
| 155 | if( zName==0 ){ |
| 156 | zFailReason = "no path beyond /ext"; |
| 157 | goto ext_not_found; |
| 158 | } |
| 159 | zFailReason = ext_pathname_ok(zName); |
| 160 | if( zFailReason ) goto ext_not_found; |
| 161 | zFailReason = "???"; |
| 162 | if( file_isdir(g.zExtRoot,ExtFILE)!=1 ){ |
| 163 | zFailReason = "extroot is not a directory"; |
| 164 | goto ext_not_found; |
| 165 | } |
| 166 | zPath = mprintf("%s/%s", g.zExtRoot, zName); |
| @@ -297,5 +312,71 @@ | |
| 312 | @ <p>Reason for failure: %h(zFailReason)</p> |
| 313 | } |
| 314 | } |
| 315 | return; |
| 316 | } |
| 317 | |
| 318 | /* |
| 319 | ** Create a temporary SFILE table and fill it with one entry for each file |
| 320 | ** in the extension document root directory (g.zExtRoot). The SFILE table |
| 321 | ** looks like this: |
| 322 | ** |
| 323 | ** CREATE TEMP TABLE sfile( |
| 324 | ** pathname TEXT PRIMARY KEY, |
| 325 | ** isexe BOOLEAN |
| 326 | ** ) WITHOUT ROWID; |
| 327 | */ |
| 328 | void ext_files(void){ |
| 329 | Blob base; |
| 330 | db_multi_exec( |
| 331 | "CREATE TEMP TABLE sfile(\n" |
| 332 | " pathname TEXT PRIMARY KEY,\n" |
| 333 | " isexe BOOLEAN\n" |
| 334 | ") WITHOUT ROWID;" |
| 335 | ); |
| 336 | blob_init(&base, g.zExtRoot, -1); |
| 337 | vfile_scan(&base, blob_size(&base), |
| 338 | SCAN_ALL|SCAN_ISEXE, |
| 339 | 0, 0, ExtFILE); |
| 340 | blob_zero(&base); |
| 341 | } |
| 342 | |
| 343 | /* |
| 344 | ** WEBPAGE: extfilelist |
| 345 | ** |
| 346 | ** List all files in the extension CGI document root and its subfolders. |
| 347 | */ |
| 348 | void ext_filelist_page(void){ |
| 349 | Stmt q; |
| 350 | login_check_credentials(); |
| 351 | if( !g.perm.Admin ){ |
| 352 | login_needed(0); |
| 353 | return; |
| 354 | } |
| 355 | ext_files(); |
| 356 | style_header("CGI Extension Filelist"); |
| 357 | @ <table border="0" cellspacing="0" cellpadding="3"> |
| 358 | @ <tbody> |
| 359 | db_prepare(&q, "SELECT pathname, isexe FROM sfile" |
| 360 | " ORDER BY pathname"); |
| 361 | while( db_step(&q)==SQLITE_ROW ){ |
| 362 | const char *zName = db_column_text(&q,0); |
| 363 | int isExe = db_column_int(&q,1); |
| 364 | @ <tr> |
| 365 | if( ext_pathname_ok(zName)!=0 ){ |
| 366 | @ <td><span style="opacity:0.5;">%h(zName)</span></td> |
| 367 | @ <td>data file</td> |
| 368 | }else{ |
| 369 | @ <td><a href="%R/ext/%h(zName)">%h(zName)</a></td> |
| 370 | if( isExe ){ |
| 371 | @ <td>CGI</td> |
| 372 | }else{ |
| 373 | @ <td>static content</td> |
| 374 | } |
| 375 | } |
| 376 | @ </tr> |
| 377 | } |
| 378 | db_finalize(&q); |
| 379 | @ </tbody> |
| 380 | @ </table> |
| 381 | style_footer(); |
| 382 | } |
| 383 |
+2
| --- src/main.c | ||
| +++ src/main.c | ||
| @@ -148,10 +148,11 @@ | ||
| 148 | 148 | unsigned iRepoDataVers; /* Initial data version for repository database */ |
| 149 | 149 | char *zRepositoryOption; /* Most recent cached repository option value */ |
| 150 | 150 | char *zRepositoryName; /* Name of the repository database file */ |
| 151 | 151 | char *zLocalDbName; /* Name of the local database file */ |
| 152 | 152 | char *zOpenRevision; /* Check-in version to use during database open */ |
| 153 | + char *zCmdName; /* Name of the Fossil command currently running */ | |
| 153 | 154 | int localOpen; /* True if the local database is open */ |
| 154 | 155 | char *zLocalRoot; /* The directory holding the local database */ |
| 155 | 156 | int minPrefix; /* Number of digits needed for a distinct UUID */ |
| 156 | 157 | int eHashPolicy; /* Current hash policy. One of HPOLICY_* */ |
| 157 | 158 | int fSqlTrace; /* True if --sqltrace flag is present */ |
| @@ -786,10 +787,11 @@ | ||
| 786 | 787 | fossil_panic("file descriptor 2 is not open. (fd=%d, errno=%d)", |
| 787 | 788 | fd, x); |
| 788 | 789 | } |
| 789 | 790 | } |
| 790 | 791 | #endif |
| 792 | + g.zCmdName = zCmdName; | |
| 791 | 793 | rc = dispatch_name_search(zCmdName, CMDFLAG_COMMAND|CMDFLAG_PREFIX, &pCmd); |
| 792 | 794 | if( rc==1 ){ |
| 793 | 795 | #ifdef FOSSIL_ENABLE_TH1_HOOKS |
| 794 | 796 | if( !g.isHTTP && !g.fNoThHook ){ |
| 795 | 797 | rc = Th_CommandHook(zCmdName, 0); |
| 796 | 798 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -148,10 +148,11 @@ | |
| 148 | unsigned iRepoDataVers; /* Initial data version for repository database */ |
| 149 | char *zRepositoryOption; /* Most recent cached repository option value */ |
| 150 | char *zRepositoryName; /* Name of the repository database file */ |
| 151 | char *zLocalDbName; /* Name of the local database file */ |
| 152 | char *zOpenRevision; /* Check-in version to use during database open */ |
| 153 | int localOpen; /* True if the local database is open */ |
| 154 | char *zLocalRoot; /* The directory holding the local database */ |
| 155 | int minPrefix; /* Number of digits needed for a distinct UUID */ |
| 156 | int eHashPolicy; /* Current hash policy. One of HPOLICY_* */ |
| 157 | int fSqlTrace; /* True if --sqltrace flag is present */ |
| @@ -786,10 +787,11 @@ | |
| 786 | fossil_panic("file descriptor 2 is not open. (fd=%d, errno=%d)", |
| 787 | fd, x); |
| 788 | } |
| 789 | } |
| 790 | #endif |
| 791 | rc = dispatch_name_search(zCmdName, CMDFLAG_COMMAND|CMDFLAG_PREFIX, &pCmd); |
| 792 | if( rc==1 ){ |
| 793 | #ifdef FOSSIL_ENABLE_TH1_HOOKS |
| 794 | if( !g.isHTTP && !g.fNoThHook ){ |
| 795 | rc = Th_CommandHook(zCmdName, 0); |
| 796 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -148,10 +148,11 @@ | |
| 148 | unsigned iRepoDataVers; /* Initial data version for repository database */ |
| 149 | char *zRepositoryOption; /* Most recent cached repository option value */ |
| 150 | char *zRepositoryName; /* Name of the repository database file */ |
| 151 | char *zLocalDbName; /* Name of the local database file */ |
| 152 | char *zOpenRevision; /* Check-in version to use during database open */ |
| 153 | char *zCmdName; /* Name of the Fossil command currently running */ |
| 154 | int localOpen; /* True if the local database is open */ |
| 155 | char *zLocalRoot; /* The directory holding the local database */ |
| 156 | int minPrefix; /* Number of digits needed for a distinct UUID */ |
| 157 | int eHashPolicy; /* Current hash policy. One of HPOLICY_* */ |
| 158 | int fSqlTrace; /* True if --sqltrace flag is present */ |
| @@ -786,10 +787,11 @@ | |
| 787 | fossil_panic("file descriptor 2 is not open. (fd=%d, errno=%d)", |
| 788 | fd, x); |
| 789 | } |
| 790 | } |
| 791 | #endif |
| 792 | g.zCmdName = zCmdName; |
| 793 | rc = dispatch_name_search(zCmdName, CMDFLAG_COMMAND|CMDFLAG_PREFIX, &pCmd); |
| 794 | if( rc==1 ){ |
| 795 | #ifdef FOSSIL_ENABLE_TH1_HOOKS |
| 796 | if( !g.isHTTP && !g.fNoThHook ){ |
| 797 | rc = Th_CommandHook(zCmdName, 0); |
| 798 |
+1
-1
| --- src/repolist.c | ||
| +++ src/repolist.c | ||
| @@ -133,11 +133,11 @@ | ||
| 133 | 133 | */ |
| 134 | 134 | blob_init(&base, g.zRepositoryName, -1); |
| 135 | 135 | sqlite3_open(":memory:", &g.db); |
| 136 | 136 | db_multi_exec("CREATE TABLE sfile(pathname TEXT);"); |
| 137 | 137 | db_multi_exec("CREATE TABLE vfile(pathname);"); |
| 138 | - vfile_scan(&base, blob_size(&base), 0, 0, 0); | |
| 138 | + vfile_scan(&base, blob_size(&base), 0, 0, 0, ExtFILE); | |
| 139 | 139 | db_multi_exec("DELETE FROM sfile WHERE pathname NOT GLOB '*[^/].fossil'"); |
| 140 | 140 | allRepo = 0; |
| 141 | 141 | } |
| 142 | 142 | n = db_int(0, "SELECT count(*) FROM sfile"); |
| 143 | 143 | if( n==0 ){ |
| 144 | 144 |
| --- src/repolist.c | |
| +++ src/repolist.c | |
| @@ -133,11 +133,11 @@ | |
| 133 | */ |
| 134 | blob_init(&base, g.zRepositoryName, -1); |
| 135 | sqlite3_open(":memory:", &g.db); |
| 136 | db_multi_exec("CREATE TABLE sfile(pathname TEXT);"); |
| 137 | db_multi_exec("CREATE TABLE vfile(pathname);"); |
| 138 | vfile_scan(&base, blob_size(&base), 0, 0, 0); |
| 139 | db_multi_exec("DELETE FROM sfile WHERE pathname NOT GLOB '*[^/].fossil'"); |
| 140 | allRepo = 0; |
| 141 | } |
| 142 | n = db_int(0, "SELECT count(*) FROM sfile"); |
| 143 | if( n==0 ){ |
| 144 |
| --- src/repolist.c | |
| +++ src/repolist.c | |
| @@ -133,11 +133,11 @@ | |
| 133 | */ |
| 134 | blob_init(&base, g.zRepositoryName, -1); |
| 135 | sqlite3_open(":memory:", &g.db); |
| 136 | db_multi_exec("CREATE TABLE sfile(pathname TEXT);"); |
| 137 | db_multi_exec("CREATE TABLE vfile(pathname);"); |
| 138 | vfile_scan(&base, blob_size(&base), 0, 0, 0, ExtFILE); |
| 139 | db_multi_exec("DELETE FROM sfile WHERE pathname NOT GLOB '*[^/].fossil'"); |
| 140 | allRepo = 0; |
| 141 | } |
| 142 | n = db_int(0, "SELECT count(*) FROM sfile"); |
| 143 | if( n==0 ){ |
| 144 |
+15
-8
| --- src/security_audit.c | ||
| +++ src/security_audit.c | ||
| @@ -402,17 +402,18 @@ | ||
| 402 | 402 | #endif |
| 403 | 403 | |
| 404 | 404 | if( g.zErrlog==0 || fossil_strcmp(g.zErrlog,"-")==0 ){ |
| 405 | 405 | @ <li><p> |
| 406 | 406 | @ The server error log is disabled. |
| 407 | - @ To set up an error log: | |
| 408 | - @ <ul> | |
| 409 | - @ <li>If running from CGI, make an entry "errorlog: <i>FILENAME</i>" | |
| 410 | - @ in the CGI script. | |
| 411 | - @ <li>If running the "fossil server" or "fossil http" commands, | |
| 412 | - @ add the "--errorlog <i>FILENAME</i>" command-line option. | |
| 413 | - @ </ul> | |
| 407 | + @ To set up an error log, | |
| 408 | + if( fossil_strcmp(g.zCmdName, "cgi")==0 ){ | |
| 409 | + @ make an entry like "errorlog: <i>FILENAME</i>" in the | |
| 410 | + @ CGI script at %h(P("SCRIPT_FILENAME")). | |
| 411 | + }else{ | |
| 412 | + @ add the "--errorlog <i>FILENAME</i>" option to the | |
| 413 | + @ "%h(g.argv[0]) %h(g.zCmdName)" command that launched this server. | |
| 414 | + } | |
| 414 | 415 | }else{ |
| 415 | 416 | FILE *pTest = fossil_fopen(g.zErrlog,"a"); |
| 416 | 417 | if( pTest==0 ){ |
| 417 | 418 | @ <li><p> |
| 418 | 419 | @ <b>Error:</b> |
| @@ -425,12 +426,18 @@ | ||
| 425 | 426 | @ %,lld(file_size(g.zErrlog, ExtFILE)) bytes in size. |
| 426 | 427 | } |
| 427 | 428 | } |
| 428 | 429 | |
| 429 | 430 | if( g.zExtRoot ){ |
| 431 | + int nFile; | |
| 432 | + int nCgi; | |
| 433 | + ext_files(); | |
| 434 | + nFile = db_int(0, "SELECT count(*) FROM sfile"); | |
| 435 | + nCgi = nFile==0 ? 0 : db_int(0,"SELECT count(*) FROM sfile WHERE isexe"); | |
| 430 | 436 | @ <li><p> CGI Extensions are enabled with a document root |
| 431 | - @ of <b>%h(g.zExtRoot)</b>. | |
| 437 | + @ at <a href='%R/extfilelist'>%h(g.zExtRoot)</a> holding | |
| 438 | + @ %d(nCgi) CGIs and %d(nFile-nCgi) static content and data files. | |
| 432 | 439 | } |
| 433 | 440 | |
| 434 | 441 | @ <li><p> User capability summary: |
| 435 | 442 | capability_summary(); |
| 436 | 443 | |
| 437 | 444 |
| --- src/security_audit.c | |
| +++ src/security_audit.c | |
| @@ -402,17 +402,18 @@ | |
| 402 | #endif |
| 403 | |
| 404 | if( g.zErrlog==0 || fossil_strcmp(g.zErrlog,"-")==0 ){ |
| 405 | @ <li><p> |
| 406 | @ The server error log is disabled. |
| 407 | @ To set up an error log: |
| 408 | @ <ul> |
| 409 | @ <li>If running from CGI, make an entry "errorlog: <i>FILENAME</i>" |
| 410 | @ in the CGI script. |
| 411 | @ <li>If running the "fossil server" or "fossil http" commands, |
| 412 | @ add the "--errorlog <i>FILENAME</i>" command-line option. |
| 413 | @ </ul> |
| 414 | }else{ |
| 415 | FILE *pTest = fossil_fopen(g.zErrlog,"a"); |
| 416 | if( pTest==0 ){ |
| 417 | @ <li><p> |
| 418 | @ <b>Error:</b> |
| @@ -425,12 +426,18 @@ | |
| 425 | @ %,lld(file_size(g.zErrlog, ExtFILE)) bytes in size. |
| 426 | } |
| 427 | } |
| 428 | |
| 429 | if( g.zExtRoot ){ |
| 430 | @ <li><p> CGI Extensions are enabled with a document root |
| 431 | @ of <b>%h(g.zExtRoot)</b>. |
| 432 | } |
| 433 | |
| 434 | @ <li><p> User capability summary: |
| 435 | capability_summary(); |
| 436 | |
| 437 |
| --- src/security_audit.c | |
| +++ src/security_audit.c | |
| @@ -402,17 +402,18 @@ | |
| 402 | #endif |
| 403 | |
| 404 | if( g.zErrlog==0 || fossil_strcmp(g.zErrlog,"-")==0 ){ |
| 405 | @ <li><p> |
| 406 | @ The server error log is disabled. |
| 407 | @ To set up an error log, |
| 408 | if( fossil_strcmp(g.zCmdName, "cgi")==0 ){ |
| 409 | @ make an entry like "errorlog: <i>FILENAME</i>" in the |
| 410 | @ CGI script at %h(P("SCRIPT_FILENAME")). |
| 411 | }else{ |
| 412 | @ add the "--errorlog <i>FILENAME</i>" option to the |
| 413 | @ "%h(g.argv[0]) %h(g.zCmdName)" command that launched this server. |
| 414 | } |
| 415 | }else{ |
| 416 | FILE *pTest = fossil_fopen(g.zErrlog,"a"); |
| 417 | if( pTest==0 ){ |
| 418 | @ <li><p> |
| 419 | @ <b>Error:</b> |
| @@ -425,12 +426,18 @@ | |
| 426 | @ %,lld(file_size(g.zErrlog, ExtFILE)) bytes in size. |
| 427 | } |
| 428 | } |
| 429 | |
| 430 | if( g.zExtRoot ){ |
| 431 | int nFile; |
| 432 | int nCgi; |
| 433 | ext_files(); |
| 434 | nFile = db_int(0, "SELECT count(*) FROM sfile"); |
| 435 | nCgi = nFile==0 ? 0 : db_int(0,"SELECT count(*) FROM sfile WHERE isexe"); |
| 436 | @ <li><p> CGI Extensions are enabled with a document root |
| 437 | @ at <a href='%R/extfilelist'>%h(g.zExtRoot)</a> holding |
| 438 | @ %d(nCgi) CGIs and %d(nFile-nCgi) static content and data files. |
| 439 | } |
| 440 | |
| 441 | @ <li><p> User capability summary: |
| 442 | capability_summary(); |
| 443 | |
| 444 |
+5
-1
| --- src/stat.c | ||
| +++ src/stat.c | ||
| @@ -104,11 +104,15 @@ | ||
| 104 | 104 | nDPend = db_int(0,"SELECT count(*) FROM pending_alert" |
| 105 | 105 | " WHERE NOT sentDigest"); |
| 106 | 106 | @ <tr><th>Pending Alerts:</th><td> |
| 107 | 107 | @ %,d(nPend) normal, %,d(nDPend) digest |
| 108 | 108 | @ </td></tr> |
| 109 | - @ <tr><th>Subscribers:</th><td> | |
| 109 | + if( g.perm.Admin ){ | |
| 110 | + @ <tr><th><a href="%R/subscribers">Subscribers:</a></th><td> | |
| 111 | + }else{ | |
| 112 | + @ <tr><th>Subscribers:</th><td> | |
| 113 | + } | |
| 110 | 114 | nSub = db_int(0, "SELECT count(*) FROM subscriber"); |
| 111 | 115 | nASub = db_int(0, "SELECT count(*) FROM subscriber WHERE sverified" |
| 112 | 116 | " AND NOT sdonotcall AND length(ssub)>1"); |
| 113 | 117 | @ %,d(nASub) active, %,d(nSub) total |
| 114 | 118 | @ </td></tr> |
| 115 | 119 |
| --- src/stat.c | |
| +++ src/stat.c | |
| @@ -104,11 +104,15 @@ | |
| 104 | nDPend = db_int(0,"SELECT count(*) FROM pending_alert" |
| 105 | " WHERE NOT sentDigest"); |
| 106 | @ <tr><th>Pending Alerts:</th><td> |
| 107 | @ %,d(nPend) normal, %,d(nDPend) digest |
| 108 | @ </td></tr> |
| 109 | @ <tr><th>Subscribers:</th><td> |
| 110 | nSub = db_int(0, "SELECT count(*) FROM subscriber"); |
| 111 | nASub = db_int(0, "SELECT count(*) FROM subscriber WHERE sverified" |
| 112 | " AND NOT sdonotcall AND length(ssub)>1"); |
| 113 | @ %,d(nASub) active, %,d(nSub) total |
| 114 | @ </td></tr> |
| 115 |
| --- src/stat.c | |
| +++ src/stat.c | |
| @@ -104,11 +104,15 @@ | |
| 104 | nDPend = db_int(0,"SELECT count(*) FROM pending_alert" |
| 105 | " WHERE NOT sentDigest"); |
| 106 | @ <tr><th>Pending Alerts:</th><td> |
| 107 | @ %,d(nPend) normal, %,d(nDPend) digest |
| 108 | @ </td></tr> |
| 109 | if( g.perm.Admin ){ |
| 110 | @ <tr><th><a href="%R/subscribers">Subscribers:</a></th><td> |
| 111 | }else{ |
| 112 | @ <tr><th>Subscribers:</th><td> |
| 113 | } |
| 114 | nSub = db_int(0, "SELECT count(*) FROM subscriber"); |
| 115 | nASub = db_int(0, "SELECT count(*) FROM subscriber WHERE sverified" |
| 116 | " AND NOT sdonotcall AND length(ssub)>1"); |
| 117 | @ %,d(nASub) active, %,d(nSub) total |
| 118 | @ </td></tr> |
| 119 |
+43
-22
| --- src/vfile.c | ||
| +++ src/vfile.c | ||
| @@ -430,19 +430,21 @@ | ||
| 430 | 430 | #define SCAN_ALL 0x001 /* Includes files that begin with "." */ |
| 431 | 431 | #define SCAN_TEMP 0x002 /* Only Fossil-generated files like *-baseline */ |
| 432 | 432 | #define SCAN_NESTED 0x004 /* Scan for empty dirs in nested checkouts */ |
| 433 | 433 | #define SCAN_MTIME 0x008 /* Populate mtime column */ |
| 434 | 434 | #define SCAN_SIZE 0x010 /* Populate size column */ |
| 435 | +#define SCAN_ISEXE 0x020 /* Populate isexe column */ | |
| 435 | 436 | #endif /* INTERFACE */ |
| 436 | 437 | |
| 437 | 438 | /* |
| 438 | 439 | ** Load into table SFILE the name of every ordinary file in |
| 439 | 440 | ** the directory pPath. Omit the first nPrefix characters of |
| 440 | 441 | ** of pPath when inserting into the SFILE table. |
| 441 | -** | |
| 442 | 442 | ** Subdirectories are scanned recursively. |
| 443 | -** Omit files named in VFILE. | |
| 443 | +** | |
| 444 | +** Omit files named in VFILE if eFType==RepoFILE. Include all files | |
| 445 | +** if eFType==ExtFILE. | |
| 444 | 446 | ** |
| 445 | 447 | ** Files whose names begin with "." are omitted unless the SCAN_ALL |
| 446 | 448 | ** flag is set. |
| 447 | 449 | ** |
| 448 | 450 | ** Any files or directories that match the glob patterns pIgnore* |
| @@ -452,11 +454,12 @@ | ||
| 452 | 454 | void vfile_scan( |
| 453 | 455 | Blob *pPath, /* Directory to be scanned */ |
| 454 | 456 | int nPrefix, /* Number of bytes in directory name */ |
| 455 | 457 | unsigned scanFlags, /* Zero or more SCAN_xxx flags */ |
| 456 | 458 | Glob *pIgnore1, /* Do not add files that match this GLOB */ |
| 457 | - Glob *pIgnore2 /* Omit files matching this GLOB too */ | |
| 459 | + Glob *pIgnore2, /* Omit files matching this GLOB too */ | |
| 460 | + int eFType /* ExtFILE or RepoFILE */ | |
| 458 | 461 | ){ |
| 459 | 462 | DIR *d; |
| 460 | 463 | int origSize; |
| 461 | 464 | struct dirent *pEntry; |
| 462 | 465 | int skipAll = 0; |
| @@ -472,20 +475,34 @@ | ||
| 472 | 475 | blob_resize(pPath, origSize); |
| 473 | 476 | } |
| 474 | 477 | if( skipAll ) return; |
| 475 | 478 | |
| 476 | 479 | if( depth==0 ){ |
| 477 | - db_prepare(&ins, | |
| 478 | - "INSERT OR IGNORE INTO sfile(pathname%s%s) SELECT :file%s%s" | |
| 479 | - " WHERE NOT EXISTS(SELECT 1 FROM vfile WHERE" | |
| 480 | - " pathname=:file %s)", | |
| 481 | - scanFlags & SCAN_MTIME ? ", mtime" : "", | |
| 482 | - scanFlags & SCAN_SIZE ? ", size" : "", | |
| 483 | - scanFlags & SCAN_MTIME ? ", :mtime" : "", | |
| 484 | - scanFlags & SCAN_SIZE ? ", :size" : "", | |
| 485 | - filename_collation() | |
| 486 | - ); | |
| 480 | + if( eFType==ExtFILE ){ | |
| 481 | + db_prepare(&ins, | |
| 482 | + "INSERT OR IGNORE INTO sfile(pathname%s%s%s) VALUES(:file%s%s%s)", | |
| 483 | + scanFlags & SCAN_MTIME ? ",mtime" : "", | |
| 484 | + scanFlags & SCAN_SIZE ? ",size" : "", | |
| 485 | + scanFlags & SCAN_ISEXE ? ",isexe" : "", | |
| 486 | + scanFlags & SCAN_MTIME ? ",:mtime" : "", | |
| 487 | + scanFlags & SCAN_SIZE ? ",:size" : "", | |
| 488 | + scanFlags & SCAN_ISEXE ? ",:isexe" : "" | |
| 489 | + ); | |
| 490 | + }else{ | |
| 491 | + db_prepare(&ins, | |
| 492 | + "INSERT OR IGNORE INTO sfile(pathname%s%s%s) SELECT :file%s%s%s" | |
| 493 | + " WHERE NOT EXISTS(SELECT 1 FROM vfile WHERE" | |
| 494 | + " pathname=:file %s)", | |
| 495 | + scanFlags & SCAN_MTIME ? ",mtime" : "", | |
| 496 | + scanFlags & SCAN_SIZE ? ",size" : "", | |
| 497 | + scanFlags & SCAN_ISEXE ? ",isexe" : "", | |
| 498 | + scanFlags & SCAN_MTIME ? ",:mtime" : "", | |
| 499 | + scanFlags & SCAN_SIZE ? ",:size" : "", | |
| 500 | + scanFlags & SCAN_ISEXE ? ",:isexe" : "", | |
| 501 | + filename_collation() | |
| 502 | + ); | |
| 503 | + } | |
| 487 | 504 | } |
| 488 | 505 | depth++; |
| 489 | 506 | |
| 490 | 507 | zNative = fossil_utf8_to_path(blob_str(pPath), 1); |
| 491 | 508 | d = opendir(zNative); |
| @@ -504,16 +521,16 @@ | ||
| 504 | 521 | if( glob_match(pIgnore1, &zPath[nPrefix+1]) || |
| 505 | 522 | glob_match(pIgnore2, &zPath[nPrefix+1]) ){ |
| 506 | 523 | /* do nothing */ |
| 507 | 524 | #ifdef _DIRENT_HAVE_D_TYPE |
| 508 | 525 | }else if( (pEntry->d_type==DT_UNKNOWN || pEntry->d_type==DT_LNK) |
| 509 | - ? (file_isdir(zPath, RepoFILE)==1) : (pEntry->d_type==DT_DIR) ){ | |
| 526 | + ? (file_isdir(zPath, eFType)==1) : (pEntry->d_type==DT_DIR) ){ | |
| 510 | 527 | #else |
| 511 | - }else if( file_isdir(zPath, RepoFILE)==1 ){ | |
| 528 | + }else if( file_isdir(zPath, eFType)==1 ){ | |
| 512 | 529 | #endif |
| 513 | 530 | if( !vfile_top_of_checkout(zPath) ){ |
| 514 | - vfile_scan(pPath, nPrefix, scanFlags, pIgnore1, pIgnore2); | |
| 531 | + vfile_scan(pPath, nPrefix, scanFlags, pIgnore1, pIgnore2, eFType); | |
| 515 | 532 | } |
| 516 | 533 | #ifdef _DIRENT_HAVE_D_TYPE |
| 517 | 534 | }else if( (pEntry->d_type==DT_UNKNOWN || pEntry->d_type==DT_LNK) |
| 518 | 535 | ? (file_isfile_or_link(zPath)) : (pEntry->d_type==DT_REG) ){ |
| 519 | 536 | #else |
| @@ -520,14 +537,17 @@ | ||
| 520 | 537 | }else if( file_isfile_or_link(zPath) ){ |
| 521 | 538 | #endif |
| 522 | 539 | if( (scanFlags & SCAN_TEMP)==0 || is_temporary_file(zUtf8) ){ |
| 523 | 540 | db_bind_text(&ins, ":file", &zPath[nPrefix+1]); |
| 524 | 541 | if( scanFlags & SCAN_MTIME ){ |
| 525 | - db_bind_int(&ins, ":mtime", file_mtime(zPath, RepoFILE)); | |
| 542 | + db_bind_int(&ins, ":mtime", file_mtime(zPath, eFType)); | |
| 526 | 543 | } |
| 527 | 544 | if( scanFlags & SCAN_SIZE ){ |
| 528 | - db_bind_int(&ins, ":size", file_size(zPath, RepoFILE)); | |
| 545 | + db_bind_int(&ins, ":size", file_size(zPath, eFType)); | |
| 546 | + } | |
| 547 | + if( scanFlags & SCAN_ISEXE ){ | |
| 548 | + db_bind_int(&ins, ":isexe", file_isexe(zPath, eFType)); | |
| 529 | 549 | } |
| 530 | 550 | db_step(&ins); |
| 531 | 551 | db_reset(&ins); |
| 532 | 552 | } |
| 533 | 553 | } |
| @@ -564,11 +584,12 @@ | ||
| 564 | 584 | int vfile_dir_scan( |
| 565 | 585 | Blob *pPath, /* Base directory to be scanned */ |
| 566 | 586 | int nPrefix, /* Number of bytes in base directory name */ |
| 567 | 587 | unsigned scanFlags, /* Zero or more SCAN_xxx flags */ |
| 568 | 588 | Glob *pIgnore1, /* Do not add directories that match this GLOB */ |
| 569 | - Glob *pIgnore2 /* Omit directories matching this GLOB too */ | |
| 589 | + Glob *pIgnore2, /* Omit directories matching this GLOB too */ | |
| 590 | + int eFType /* ExtFILE or RepoFILE */ | |
| 570 | 591 | ){ |
| 571 | 592 | int result = 0; |
| 572 | 593 | DIR *d; |
| 573 | 594 | int origSize; |
| 574 | 595 | struct dirent *pEntry; |
| @@ -624,18 +645,18 @@ | ||
| 624 | 645 | if( glob_match(pIgnore1, &zPath[nPrefix+1]) || |
| 625 | 646 | glob_match(pIgnore2, &zPath[nPrefix+1]) ){ |
| 626 | 647 | /* do nothing */ |
| 627 | 648 | #ifdef _DIRENT_HAVE_D_TYPE |
| 628 | 649 | }else if( (pEntry->d_type==DT_UNKNOWN || pEntry->d_type==DT_LNK) |
| 629 | - ? (file_isdir(zPath, RepoFILE)==1) : (pEntry->d_type==DT_DIR) ){ | |
| 650 | + ? (file_isdir(zPath, eFType)==1) : (pEntry->d_type==DT_DIR) ){ | |
| 630 | 651 | #else |
| 631 | - }else if( file_isdir(zPath, RepoFILE)==1 ){ | |
| 652 | + }else if( file_isdir(zPath, eFType)==1 ){ | |
| 632 | 653 | #endif |
| 633 | 654 | if( (scanFlags & SCAN_NESTED) || !vfile_top_of_checkout(zPath) ){ |
| 634 | 655 | char *zSavePath = mprintf("%s", zPath); |
| 635 | 656 | int count = vfile_dir_scan(pPath, nPrefix, scanFlags, pIgnore1, |
| 636 | - pIgnore2); | |
| 657 | + pIgnore2, eFType); | |
| 637 | 658 | db_bind_text(&ins, ":file", &zSavePath[nPrefix+1]); |
| 638 | 659 | db_bind_int(&ins, ":count", count); |
| 639 | 660 | db_step(&ins); |
| 640 | 661 | db_reset(&ins); |
| 641 | 662 | fossil_free(zSavePath); |
| 642 | 663 |
| --- src/vfile.c | |
| +++ src/vfile.c | |
| @@ -430,19 +430,21 @@ | |
| 430 | #define SCAN_ALL 0x001 /* Includes files that begin with "." */ |
| 431 | #define SCAN_TEMP 0x002 /* Only Fossil-generated files like *-baseline */ |
| 432 | #define SCAN_NESTED 0x004 /* Scan for empty dirs in nested checkouts */ |
| 433 | #define SCAN_MTIME 0x008 /* Populate mtime column */ |
| 434 | #define SCAN_SIZE 0x010 /* Populate size column */ |
| 435 | #endif /* INTERFACE */ |
| 436 | |
| 437 | /* |
| 438 | ** Load into table SFILE the name of every ordinary file in |
| 439 | ** the directory pPath. Omit the first nPrefix characters of |
| 440 | ** of pPath when inserting into the SFILE table. |
| 441 | ** |
| 442 | ** Subdirectories are scanned recursively. |
| 443 | ** Omit files named in VFILE. |
| 444 | ** |
| 445 | ** Files whose names begin with "." are omitted unless the SCAN_ALL |
| 446 | ** flag is set. |
| 447 | ** |
| 448 | ** Any files or directories that match the glob patterns pIgnore* |
| @@ -452,11 +454,12 @@ | |
| 452 | void vfile_scan( |
| 453 | Blob *pPath, /* Directory to be scanned */ |
| 454 | int nPrefix, /* Number of bytes in directory name */ |
| 455 | unsigned scanFlags, /* Zero or more SCAN_xxx flags */ |
| 456 | Glob *pIgnore1, /* Do not add files that match this GLOB */ |
| 457 | Glob *pIgnore2 /* Omit files matching this GLOB too */ |
| 458 | ){ |
| 459 | DIR *d; |
| 460 | int origSize; |
| 461 | struct dirent *pEntry; |
| 462 | int skipAll = 0; |
| @@ -472,20 +475,34 @@ | |
| 472 | blob_resize(pPath, origSize); |
| 473 | } |
| 474 | if( skipAll ) return; |
| 475 | |
| 476 | if( depth==0 ){ |
| 477 | db_prepare(&ins, |
| 478 | "INSERT OR IGNORE INTO sfile(pathname%s%s) SELECT :file%s%s" |
| 479 | " WHERE NOT EXISTS(SELECT 1 FROM vfile WHERE" |
| 480 | " pathname=:file %s)", |
| 481 | scanFlags & SCAN_MTIME ? ", mtime" : "", |
| 482 | scanFlags & SCAN_SIZE ? ", size" : "", |
| 483 | scanFlags & SCAN_MTIME ? ", :mtime" : "", |
| 484 | scanFlags & SCAN_SIZE ? ", :size" : "", |
| 485 | filename_collation() |
| 486 | ); |
| 487 | } |
| 488 | depth++; |
| 489 | |
| 490 | zNative = fossil_utf8_to_path(blob_str(pPath), 1); |
| 491 | d = opendir(zNative); |
| @@ -504,16 +521,16 @@ | |
| 504 | if( glob_match(pIgnore1, &zPath[nPrefix+1]) || |
| 505 | glob_match(pIgnore2, &zPath[nPrefix+1]) ){ |
| 506 | /* do nothing */ |
| 507 | #ifdef _DIRENT_HAVE_D_TYPE |
| 508 | }else if( (pEntry->d_type==DT_UNKNOWN || pEntry->d_type==DT_LNK) |
| 509 | ? (file_isdir(zPath, RepoFILE)==1) : (pEntry->d_type==DT_DIR) ){ |
| 510 | #else |
| 511 | }else if( file_isdir(zPath, RepoFILE)==1 ){ |
| 512 | #endif |
| 513 | if( !vfile_top_of_checkout(zPath) ){ |
| 514 | vfile_scan(pPath, nPrefix, scanFlags, pIgnore1, pIgnore2); |
| 515 | } |
| 516 | #ifdef _DIRENT_HAVE_D_TYPE |
| 517 | }else if( (pEntry->d_type==DT_UNKNOWN || pEntry->d_type==DT_LNK) |
| 518 | ? (file_isfile_or_link(zPath)) : (pEntry->d_type==DT_REG) ){ |
| 519 | #else |
| @@ -520,14 +537,17 @@ | |
| 520 | }else if( file_isfile_or_link(zPath) ){ |
| 521 | #endif |
| 522 | if( (scanFlags & SCAN_TEMP)==0 || is_temporary_file(zUtf8) ){ |
| 523 | db_bind_text(&ins, ":file", &zPath[nPrefix+1]); |
| 524 | if( scanFlags & SCAN_MTIME ){ |
| 525 | db_bind_int(&ins, ":mtime", file_mtime(zPath, RepoFILE)); |
| 526 | } |
| 527 | if( scanFlags & SCAN_SIZE ){ |
| 528 | db_bind_int(&ins, ":size", file_size(zPath, RepoFILE)); |
| 529 | } |
| 530 | db_step(&ins); |
| 531 | db_reset(&ins); |
| 532 | } |
| 533 | } |
| @@ -564,11 +584,12 @@ | |
| 564 | int vfile_dir_scan( |
| 565 | Blob *pPath, /* Base directory to be scanned */ |
| 566 | int nPrefix, /* Number of bytes in base directory name */ |
| 567 | unsigned scanFlags, /* Zero or more SCAN_xxx flags */ |
| 568 | Glob *pIgnore1, /* Do not add directories that match this GLOB */ |
| 569 | Glob *pIgnore2 /* Omit directories matching this GLOB too */ |
| 570 | ){ |
| 571 | int result = 0; |
| 572 | DIR *d; |
| 573 | int origSize; |
| 574 | struct dirent *pEntry; |
| @@ -624,18 +645,18 @@ | |
| 624 | if( glob_match(pIgnore1, &zPath[nPrefix+1]) || |
| 625 | glob_match(pIgnore2, &zPath[nPrefix+1]) ){ |
| 626 | /* do nothing */ |
| 627 | #ifdef _DIRENT_HAVE_D_TYPE |
| 628 | }else if( (pEntry->d_type==DT_UNKNOWN || pEntry->d_type==DT_LNK) |
| 629 | ? (file_isdir(zPath, RepoFILE)==1) : (pEntry->d_type==DT_DIR) ){ |
| 630 | #else |
| 631 | }else if( file_isdir(zPath, RepoFILE)==1 ){ |
| 632 | #endif |
| 633 | if( (scanFlags & SCAN_NESTED) || !vfile_top_of_checkout(zPath) ){ |
| 634 | char *zSavePath = mprintf("%s", zPath); |
| 635 | int count = vfile_dir_scan(pPath, nPrefix, scanFlags, pIgnore1, |
| 636 | pIgnore2); |
| 637 | db_bind_text(&ins, ":file", &zSavePath[nPrefix+1]); |
| 638 | db_bind_int(&ins, ":count", count); |
| 639 | db_step(&ins); |
| 640 | db_reset(&ins); |
| 641 | fossil_free(zSavePath); |
| 642 |
| --- src/vfile.c | |
| +++ src/vfile.c | |
| @@ -430,19 +430,21 @@ | |
| 430 | #define SCAN_ALL 0x001 /* Includes files that begin with "." */ |
| 431 | #define SCAN_TEMP 0x002 /* Only Fossil-generated files like *-baseline */ |
| 432 | #define SCAN_NESTED 0x004 /* Scan for empty dirs in nested checkouts */ |
| 433 | #define SCAN_MTIME 0x008 /* Populate mtime column */ |
| 434 | #define SCAN_SIZE 0x010 /* Populate size column */ |
| 435 | #define SCAN_ISEXE 0x020 /* Populate isexe column */ |
| 436 | #endif /* INTERFACE */ |
| 437 | |
| 438 | /* |
| 439 | ** Load into table SFILE the name of every ordinary file in |
| 440 | ** the directory pPath. Omit the first nPrefix characters of |
| 441 | ** of pPath when inserting into the SFILE table. |
| 442 | ** Subdirectories are scanned recursively. |
| 443 | ** |
| 444 | ** Omit files named in VFILE if eFType==RepoFILE. Include all files |
| 445 | ** if eFType==ExtFILE. |
| 446 | ** |
| 447 | ** Files whose names begin with "." are omitted unless the SCAN_ALL |
| 448 | ** flag is set. |
| 449 | ** |
| 450 | ** Any files or directories that match the glob patterns pIgnore* |
| @@ -452,11 +454,12 @@ | |
| 454 | void vfile_scan( |
| 455 | Blob *pPath, /* Directory to be scanned */ |
| 456 | int nPrefix, /* Number of bytes in directory name */ |
| 457 | unsigned scanFlags, /* Zero or more SCAN_xxx flags */ |
| 458 | Glob *pIgnore1, /* Do not add files that match this GLOB */ |
| 459 | Glob *pIgnore2, /* Omit files matching this GLOB too */ |
| 460 | int eFType /* ExtFILE or RepoFILE */ |
| 461 | ){ |
| 462 | DIR *d; |
| 463 | int origSize; |
| 464 | struct dirent *pEntry; |
| 465 | int skipAll = 0; |
| @@ -472,20 +475,34 @@ | |
| 475 | blob_resize(pPath, origSize); |
| 476 | } |
| 477 | if( skipAll ) return; |
| 478 | |
| 479 | if( depth==0 ){ |
| 480 | if( eFType==ExtFILE ){ |
| 481 | db_prepare(&ins, |
| 482 | "INSERT OR IGNORE INTO sfile(pathname%s%s%s) VALUES(:file%s%s%s)", |
| 483 | scanFlags & SCAN_MTIME ? ",mtime" : "", |
| 484 | scanFlags & SCAN_SIZE ? ",size" : "", |
| 485 | scanFlags & SCAN_ISEXE ? ",isexe" : "", |
| 486 | scanFlags & SCAN_MTIME ? ",:mtime" : "", |
| 487 | scanFlags & SCAN_SIZE ? ",:size" : "", |
| 488 | scanFlags & SCAN_ISEXE ? ",:isexe" : "" |
| 489 | ); |
| 490 | }else{ |
| 491 | db_prepare(&ins, |
| 492 | "INSERT OR IGNORE INTO sfile(pathname%s%s%s) SELECT :file%s%s%s" |
| 493 | " WHERE NOT EXISTS(SELECT 1 FROM vfile WHERE" |
| 494 | " pathname=:file %s)", |
| 495 | scanFlags & SCAN_MTIME ? ",mtime" : "", |
| 496 | scanFlags & SCAN_SIZE ? ",size" : "", |
| 497 | scanFlags & SCAN_ISEXE ? ",isexe" : "", |
| 498 | scanFlags & SCAN_MTIME ? ",:mtime" : "", |
| 499 | scanFlags & SCAN_SIZE ? ",:size" : "", |
| 500 | scanFlags & SCAN_ISEXE ? ",:isexe" : "", |
| 501 | filename_collation() |
| 502 | ); |
| 503 | } |
| 504 | } |
| 505 | depth++; |
| 506 | |
| 507 | zNative = fossil_utf8_to_path(blob_str(pPath), 1); |
| 508 | d = opendir(zNative); |
| @@ -504,16 +521,16 @@ | |
| 521 | if( glob_match(pIgnore1, &zPath[nPrefix+1]) || |
| 522 | glob_match(pIgnore2, &zPath[nPrefix+1]) ){ |
| 523 | /* do nothing */ |
| 524 | #ifdef _DIRENT_HAVE_D_TYPE |
| 525 | }else if( (pEntry->d_type==DT_UNKNOWN || pEntry->d_type==DT_LNK) |
| 526 | ? (file_isdir(zPath, eFType)==1) : (pEntry->d_type==DT_DIR) ){ |
| 527 | #else |
| 528 | }else if( file_isdir(zPath, eFType)==1 ){ |
| 529 | #endif |
| 530 | if( !vfile_top_of_checkout(zPath) ){ |
| 531 | vfile_scan(pPath, nPrefix, scanFlags, pIgnore1, pIgnore2, eFType); |
| 532 | } |
| 533 | #ifdef _DIRENT_HAVE_D_TYPE |
| 534 | }else if( (pEntry->d_type==DT_UNKNOWN || pEntry->d_type==DT_LNK) |
| 535 | ? (file_isfile_or_link(zPath)) : (pEntry->d_type==DT_REG) ){ |
| 536 | #else |
| @@ -520,14 +537,17 @@ | |
| 537 | }else if( file_isfile_or_link(zPath) ){ |
| 538 | #endif |
| 539 | if( (scanFlags & SCAN_TEMP)==0 || is_temporary_file(zUtf8) ){ |
| 540 | db_bind_text(&ins, ":file", &zPath[nPrefix+1]); |
| 541 | if( scanFlags & SCAN_MTIME ){ |
| 542 | db_bind_int(&ins, ":mtime", file_mtime(zPath, eFType)); |
| 543 | } |
| 544 | if( scanFlags & SCAN_SIZE ){ |
| 545 | db_bind_int(&ins, ":size", file_size(zPath, eFType)); |
| 546 | } |
| 547 | if( scanFlags & SCAN_ISEXE ){ |
| 548 | db_bind_int(&ins, ":isexe", file_isexe(zPath, eFType)); |
| 549 | } |
| 550 | db_step(&ins); |
| 551 | db_reset(&ins); |
| 552 | } |
| 553 | } |
| @@ -564,11 +584,12 @@ | |
| 584 | int vfile_dir_scan( |
| 585 | Blob *pPath, /* Base directory to be scanned */ |
| 586 | int nPrefix, /* Number of bytes in base directory name */ |
| 587 | unsigned scanFlags, /* Zero or more SCAN_xxx flags */ |
| 588 | Glob *pIgnore1, /* Do not add directories that match this GLOB */ |
| 589 | Glob *pIgnore2, /* Omit directories matching this GLOB too */ |
| 590 | int eFType /* ExtFILE or RepoFILE */ |
| 591 | ){ |
| 592 | int result = 0; |
| 593 | DIR *d; |
| 594 | int origSize; |
| 595 | struct dirent *pEntry; |
| @@ -624,18 +645,18 @@ | |
| 645 | if( glob_match(pIgnore1, &zPath[nPrefix+1]) || |
| 646 | glob_match(pIgnore2, &zPath[nPrefix+1]) ){ |
| 647 | /* do nothing */ |
| 648 | #ifdef _DIRENT_HAVE_D_TYPE |
| 649 | }else if( (pEntry->d_type==DT_UNKNOWN || pEntry->d_type==DT_LNK) |
| 650 | ? (file_isdir(zPath, eFType)==1) : (pEntry->d_type==DT_DIR) ){ |
| 651 | #else |
| 652 | }else if( file_isdir(zPath, eFType)==1 ){ |
| 653 | #endif |
| 654 | if( (scanFlags & SCAN_NESTED) || !vfile_top_of_checkout(zPath) ){ |
| 655 | char *zSavePath = mprintf("%s", zPath); |
| 656 | int count = vfile_dir_scan(pPath, nPrefix, scanFlags, pIgnore1, |
| 657 | pIgnore2, eFType); |
| 658 | db_bind_text(&ins, ":file", &zSavePath[nPrefix+1]); |
| 659 | db_bind_int(&ins, ":count", count); |
| 660 | db_step(&ins); |
| 661 | db_reset(&ins); |
| 662 | fossil_free(zSavePath); |
| 663 |