Fossil SCM

Improved detection of attempts to write through a symlink. Now also works for "revert", "stash", and "undo/redo".

drh 2020-08-19 12:08 sec2020
Commit f63297b2c521f48798158ead68cbc6d908f7fb9224579f6126cedff0f6aea098
+25
--- src/file.c
+++ src/file.c
@@ -370,10 +370,35 @@
370370
i = j;
371371
}
372372
fossil_free(z);
373373
return 0;
374374
}
375
+
376
+/*
377
+** The file named zFile is suppose to be an in-tree file. Check to
378
+** ensure that it will be safe to write to this file by verifying that
379
+** there are no symlinks or other non-directory objects in between the
380
+** root of the checkout and zFile.
381
+**
382
+** If a problem is found, print a warning message (using fossil_warning())
383
+** and return non-zero. If everything is ok, return zero.
384
+*/
385
+int file_unsafe_in_tree_path(const char *zFile){
386
+ int n;
387
+ if( !file_is_absolute_path(zFile) ){
388
+ fossil_panic("%s is not an absolute pathname",zFile);
389
+ }
390
+ if( fossil_strnicmp(g.zLocalRoot, zFile, (int)strlen(g.zLocalRoot)) ){
391
+ fossil_panic("%s is not a prefix of %s", g.zLocalRoot, zFile);
392
+ }
393
+ n = file_nondir_objects_on_path(g.zLocalRoot, zFile);
394
+ if( n ){
395
+ fossil_warning("cannot write to %s because non-directory object %.*s"
396
+ " is in the way", zFile, n, zFile);
397
+ }
398
+ return n;
399
+}
375400
376401
/*
377402
** Return 1 if zFilename is a directory. Return 0 if zFilename
378403
** does not exist. Return 2 if zFilename exists but is something
379404
** other than a directory.
380405
--- src/file.c
+++ src/file.c
@@ -370,10 +370,35 @@
370 i = j;
371 }
372 fossil_free(z);
373 return 0;
374 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
375
376 /*
377 ** Return 1 if zFilename is a directory. Return 0 if zFilename
378 ** does not exist. Return 2 if zFilename exists but is something
379 ** other than a directory.
380
--- src/file.c
+++ src/file.c
@@ -370,10 +370,35 @@
370 i = j;
371 }
372 fossil_free(z);
373 return 0;
374 }
375
376 /*
377 ** The file named zFile is suppose to be an in-tree file. Check to
378 ** ensure that it will be safe to write to this file by verifying that
379 ** there are no symlinks or other non-directory objects in between the
380 ** root of the checkout and zFile.
381 **
382 ** If a problem is found, print a warning message (using fossil_warning())
383 ** and return non-zero. If everything is ok, return zero.
384 */
385 int file_unsafe_in_tree_path(const char *zFile){
386 int n;
387 if( !file_is_absolute_path(zFile) ){
388 fossil_panic("%s is not an absolute pathname",zFile);
389 }
390 if( fossil_strnicmp(g.zLocalRoot, zFile, (int)strlen(g.zLocalRoot)) ){
391 fossil_panic("%s is not a prefix of %s", g.zLocalRoot, zFile);
392 }
393 n = file_nondir_objects_on_path(g.zLocalRoot, zFile);
394 if( n ){
395 fossil_warning("cannot write to %s because non-directory object %.*s"
396 " is in the way", zFile, n, zFile);
397 }
398 return n;
399 }
400
401 /*
402 ** Return 1 if zFilename is a directory. Return 0 if zFilename
403 ** does not exist. Return 2 if zFilename exists but is something
404 ** other than a directory.
405
--- src/stash.c
+++ src/stash.c
@@ -334,10 +334,12 @@
334334
blob_write_to_file(&delta, zNPath);
335335
file_setexe(zNPath, isExec);
336336
}else if( isRemoved ){
337337
fossil_print("DELETE %s\n", zOrig);
338338
file_delete(zOPath);
339
+ }else if( file_unsafe_in_tree_path(zNPath) ){
340
+ /* Ignore the unsafe path */
339341
}else{
340342
Blob a, b, out, disk;
341343
int isNewLink = file_islink(zOPath);
342344
db_ephemeral_blob(&q, 6, &delta);
343345
blob_read_from_file(&disk, zOPath, RepoFILE);
344346
--- src/stash.c
+++ src/stash.c
@@ -334,10 +334,12 @@
334 blob_write_to_file(&delta, zNPath);
335 file_setexe(zNPath, isExec);
336 }else if( isRemoved ){
337 fossil_print("DELETE %s\n", zOrig);
338 file_delete(zOPath);
 
 
339 }else{
340 Blob a, b, out, disk;
341 int isNewLink = file_islink(zOPath);
342 db_ephemeral_blob(&q, 6, &delta);
343 blob_read_from_file(&disk, zOPath, RepoFILE);
344
--- src/stash.c
+++ src/stash.c
@@ -334,10 +334,12 @@
334 blob_write_to_file(&delta, zNPath);
335 file_setexe(zNPath, isExec);
336 }else if( isRemoved ){
337 fossil_print("DELETE %s\n", zOrig);
338 file_delete(zOPath);
339 }else if( file_unsafe_in_tree_path(zNPath) ){
340 /* Ignore the unsafe path */
341 }else{
342 Blob a, b, out, disk;
343 int isNewLink = file_islink(zOPath);
344 db_ephemeral_blob(&q, 6, &delta);
345 blob_read_from_file(&disk, zOPath, RepoFILE);
346
+4 -2
--- src/undo.c
+++ src/undo.c
@@ -52,11 +52,11 @@
5252
int new_exe;
5353
int new_link;
5454
int old_link;
5555
Blob current;
5656
Blob new;
57
- zFullname = mprintf("%s/%s", g.zLocalRoot, zPathname);
57
+ zFullname = mprintf("%s%s", g.zLocalRoot, zPathname);
5858
old_link = db_column_int(&q, 3);
5959
new_exists = file_size(zFullname, RepoFILE)>=0;
6060
new_link = file_islink(0);
6161
if( new_exists ){
6262
blob_read_from_file(&current, zFullname, RepoFILE);
@@ -69,11 +69,13 @@
6969
old_exists = db_column_int(&q, 1);
7070
old_exe = db_column_int(&q, 2);
7171
if( old_exists ){
7272
db_ephemeral_blob(&q, 0, &new);
7373
}
74
- if( old_exists ){
74
+ if( file_unsafe_in_tree_path(zFullname) ){
75
+ /* do nothign with this unsafe file */
76
+ }else if( old_exists ){
7577
if( new_exists ){
7678
fossil_print("%s %s\n", redoFlag ? "REDO" : "UNDO", zPathname);
7779
}else{
7880
fossil_print("NEW %s\n", zPathname);
7981
}
8082
--- src/undo.c
+++ src/undo.c
@@ -52,11 +52,11 @@
52 int new_exe;
53 int new_link;
54 int old_link;
55 Blob current;
56 Blob new;
57 zFullname = mprintf("%s/%s", g.zLocalRoot, zPathname);
58 old_link = db_column_int(&q, 3);
59 new_exists = file_size(zFullname, RepoFILE)>=0;
60 new_link = file_islink(0);
61 if( new_exists ){
62 blob_read_from_file(&current, zFullname, RepoFILE);
@@ -69,11 +69,13 @@
69 old_exists = db_column_int(&q, 1);
70 old_exe = db_column_int(&q, 2);
71 if( old_exists ){
72 db_ephemeral_blob(&q, 0, &new);
73 }
74 if( old_exists ){
 
 
75 if( new_exists ){
76 fossil_print("%s %s\n", redoFlag ? "REDO" : "UNDO", zPathname);
77 }else{
78 fossil_print("NEW %s\n", zPathname);
79 }
80
--- src/undo.c
+++ src/undo.c
@@ -52,11 +52,11 @@
52 int new_exe;
53 int new_link;
54 int old_link;
55 Blob current;
56 Blob new;
57 zFullname = mprintf("%s%s", g.zLocalRoot, zPathname);
58 old_link = db_column_int(&q, 3);
59 new_exists = file_size(zFullname, RepoFILE)>=0;
60 new_link = file_islink(0);
61 if( new_exists ){
62 blob_read_from_file(&current, zFullname, RepoFILE);
@@ -69,11 +69,13 @@
69 old_exists = db_column_int(&q, 1);
70 old_exe = db_column_int(&q, 2);
71 if( old_exists ){
72 db_ephemeral_blob(&q, 0, &new);
73 }
74 if( file_unsafe_in_tree_path(zFullname) ){
75 /* do nothign with this unsafe file */
76 }else if( old_exists ){
77 if( new_exists ){
78 fossil_print("%s %s\n", redoFlag ? "REDO" : "UNDO", zPathname);
79 }else{
80 fossil_print("NEW %s\n", zPathname);
81 }
82
--- src/update.c
+++ src/update.c
@@ -927,14 +927,17 @@
927927
" SET pathname=origname, origname=NULL"
928928
" WHERE pathname=%Q AND origname!=pathname;"
929929
"DELETE FROM vfile WHERE pathname=%Q",
930930
zFile, zFile
931931
);
932
+ }else if( file_unsafe_in_tree_path(zFull) ){
933
+ /* Ignore this file */
932934
}else{
933935
sqlite3_int64 mtime;
934936
int rvChnged = 0;
935937
int rvPerm = manifest_file_mperm(pRvFile);
938
+ int n;
936939
937940
/* Determine if reverted-to file is different than checked out file. */
938941
if( pCoManifest && (pCoFile = manifest_file_find(pCoManifest, zFile)) ){
939942
rvChnged = manifest_file_mperm(pRvFile)!=rvPerm
940943
|| fossil_strcmp(pRvFile->zUuid, pCoFile->zUuid)!=0;
941944
--- src/update.c
+++ src/update.c
@@ -927,14 +927,17 @@
927 " SET pathname=origname, origname=NULL"
928 " WHERE pathname=%Q AND origname!=pathname;"
929 "DELETE FROM vfile WHERE pathname=%Q",
930 zFile, zFile
931 );
 
 
932 }else{
933 sqlite3_int64 mtime;
934 int rvChnged = 0;
935 int rvPerm = manifest_file_mperm(pRvFile);
 
936
937 /* Determine if reverted-to file is different than checked out file. */
938 if( pCoManifest && (pCoFile = manifest_file_find(pCoManifest, zFile)) ){
939 rvChnged = manifest_file_mperm(pRvFile)!=rvPerm
940 || fossil_strcmp(pRvFile->zUuid, pCoFile->zUuid)!=0;
941
--- src/update.c
+++ src/update.c
@@ -927,14 +927,17 @@
927 " SET pathname=origname, origname=NULL"
928 " WHERE pathname=%Q AND origname!=pathname;"
929 "DELETE FROM vfile WHERE pathname=%Q",
930 zFile, zFile
931 );
932 }else if( file_unsafe_in_tree_path(zFull) ){
933 /* Ignore this file */
934 }else{
935 sqlite3_int64 mtime;
936 int rvChnged = 0;
937 int rvPerm = manifest_file_mperm(pRvFile);
938 int n;
939
940 /* Determine if reverted-to file is different than checked out file. */
941 if( pCoManifest && (pCoFile = manifest_file_find(pCoManifest, zFile)) ){
942 rvChnged = manifest_file_mperm(pRvFile)!=rvPerm
943 || fossil_strcmp(pRvFile->zUuid, pCoFile->zUuid)!=0;
944
+3 -6
--- src/vfile.c
+++ src/vfile.c
@@ -314,10 +314,13 @@
314314
id = db_column_int(&q, 0);
315315
zName = db_column_text(&q, 1);
316316
rid = db_column_int(&q, 2);
317317
isExe = db_column_int(&q, 3);
318318
isLink = db_column_int(&q, 4);
319
+ if( file_unsafe_in_tree_path(zName) ){
320
+ continue;
321
+ }
319322
content_get(rid, &content);
320323
if( file_is_the_same(&content, zName) ){
321324
blob_reset(&content);
322325
if( file_setexe(zName, isExe) ){
323326
db_multi_exec("UPDATE vfile SET mtime=%lld WHERE id=%d",
@@ -340,16 +343,10 @@
340343
blob_reset(&content);
341344
continue;
342345
}
343346
}
344347
if( verbose ) fossil_print("%s\n", &zName[nRepos]);
345
- n = file_nondir_objects_on_path(g.zLocalRoot, zName);
346
- if( n ){
347
- fossil_fatal("cannot write %s because "
348
- "non-directory object %.*s is in the way",
349
- zName, n, zName);
350
- }
351348
if( file_isdir(zName, RepoFILE)==1 ){
352349
/*TODO(dchest): remove directories? */
353350
fossil_fatal("%s is directory, cannot overwrite", zName);
354351
}
355352
if( file_size(zName, RepoFILE)>=0 && (isLink || file_islink(0)) ){
356353
--- src/vfile.c
+++ src/vfile.c
@@ -314,10 +314,13 @@
314 id = db_column_int(&q, 0);
315 zName = db_column_text(&q, 1);
316 rid = db_column_int(&q, 2);
317 isExe = db_column_int(&q, 3);
318 isLink = db_column_int(&q, 4);
 
 
 
319 content_get(rid, &content);
320 if( file_is_the_same(&content, zName) ){
321 blob_reset(&content);
322 if( file_setexe(zName, isExe) ){
323 db_multi_exec("UPDATE vfile SET mtime=%lld WHERE id=%d",
@@ -340,16 +343,10 @@
340 blob_reset(&content);
341 continue;
342 }
343 }
344 if( verbose ) fossil_print("%s\n", &zName[nRepos]);
345 n = file_nondir_objects_on_path(g.zLocalRoot, zName);
346 if( n ){
347 fossil_fatal("cannot write %s because "
348 "non-directory object %.*s is in the way",
349 zName, n, zName);
350 }
351 if( file_isdir(zName, RepoFILE)==1 ){
352 /*TODO(dchest): remove directories? */
353 fossil_fatal("%s is directory, cannot overwrite", zName);
354 }
355 if( file_size(zName, RepoFILE)>=0 && (isLink || file_islink(0)) ){
356
--- src/vfile.c
+++ src/vfile.c
@@ -314,10 +314,13 @@
314 id = db_column_int(&q, 0);
315 zName = db_column_text(&q, 1);
316 rid = db_column_int(&q, 2);
317 isExe = db_column_int(&q, 3);
318 isLink = db_column_int(&q, 4);
319 if( file_unsafe_in_tree_path(zName) ){
320 continue;
321 }
322 content_get(rid, &content);
323 if( file_is_the_same(&content, zName) ){
324 blob_reset(&content);
325 if( file_setexe(zName, isExe) ){
326 db_multi_exec("UPDATE vfile SET mtime=%lld WHERE id=%d",
@@ -340,16 +343,10 @@
343 blob_reset(&content);
344 continue;
345 }
346 }
347 if( verbose ) fossil_print("%s\n", &zName[nRepos]);
 
 
 
 
 
 
348 if( file_isdir(zName, RepoFILE)==1 ){
349 /*TODO(dchest): remove directories? */
350 fossil_fatal("%s is directory, cannot overwrite", zName);
351 }
352 if( file_size(zName, RepoFILE)>=0 && (isLink || file_islink(0)) ){
353

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button