Fossil SCM
When writing files to disk for a check-out, refuse to write through a symbolic link to a directory. Ticket [f9831fdef1d4edcc].
Commit
a64e384f0c53ddce4cc0b1f46a671c07917bf30d2367dbcede6101a76d0ce9d4
Parent
3ced48bdf815b96…
2 files changed
+45
-1
+1
+45
-1
| --- src/file.c | ||
| +++ src/file.c | ||
| @@ -323,10 +323,51 @@ | ||
| 323 | 323 | ** On Windows, always return False. |
| 324 | 324 | */ |
| 325 | 325 | int file_islink(const char *zFilename){ |
| 326 | 326 | return file_perm(zFilename, RepoFILE)==PERM_LNK; |
| 327 | 327 | } |
| 328 | + | |
| 329 | +/* | |
| 330 | +** Check every sub-directory of zRoot along the path to zFile. | |
| 331 | +** If any sub-directory is really an ordinary file or a symbolic link, | |
| 332 | +** then delete that object. The inputs are expected to be full | |
| 333 | +** pathnames. | |
| 334 | +** | |
| 335 | +** Example: Given inputs | |
| 336 | +** | |
| 337 | +** zRoot = /home/alice/project1 | |
| 338 | +** zFile = /home/alice/project1/main/src/js/fileA.js | |
| 339 | +** | |
| 340 | +** Look for objects in the following order: | |
| 341 | +** | |
| 342 | +** /home/alice/project/main | |
| 343 | +** /home/alice/project/main/src | |
| 344 | +** /home/alice/project/main/src/js | |
| 345 | +** | |
| 346 | +** If any of those objects exist and are something other than a directory | |
| 347 | +** then delete the object and return. | |
| 348 | +*/ | |
| 349 | +void file_delete_objects_on_path(const char *zRoot, const char *zFile){ | |
| 350 | + int i = (int)strlen(zRoot); | |
| 351 | + char *z = fossil_strdup(zFile); | |
| 352 | + assert( fossil_strnicmp(zRoot, z, i)==0 ); | |
| 353 | + if( i && zRoot[i-1]=='/' ) i--; | |
| 354 | + while( z[i]=='/' ){ | |
| 355 | + int j, rc; | |
| 356 | + for(j=i+1; z[j] && z[j]!='/'; j++){} | |
| 357 | + if( z[j]!='/' ) break; | |
| 358 | + z[j] = 0; | |
| 359 | + rc = file_isdir(z, SymFILE); | |
| 360 | + if( rc!=1 ){ | |
| 361 | + if( rc==2 ) file_delete(z); | |
| 362 | + break; | |
| 363 | + } | |
| 364 | + z[j] = '/'; | |
| 365 | + i = j; | |
| 366 | + } | |
| 367 | + fossil_free(z); | |
| 368 | +} | |
| 328 | 369 | |
| 329 | 370 | /* |
| 330 | 371 | ** Return 1 if zFilename is a directory. Return 0 if zFilename |
| 331 | 372 | ** does not exist. Return 2 if zFilename exists but is something |
| 332 | 373 | ** other than a directory. |
| @@ -570,11 +611,14 @@ | ||
| 570 | 611 | */ |
| 571 | 612 | int file_setexe(const char *zFilename, int onoff){ |
| 572 | 613 | int rc = 0; |
| 573 | 614 | #if !defined(_WIN32) |
| 574 | 615 | struct stat buf; |
| 575 | - if( fossil_stat(zFilename, &buf, RepoFILE)!=0 || S_ISLNK(buf.st_mode) ){ | |
| 616 | + if( fossil_stat(zFilename, &buf, RepoFILE)!=0 | |
| 617 | + || S_ISLNK(buf.st_mode) | |
| 618 | + || S_ISDIR(buf.st_mode) | |
| 619 | + ){ | |
| 576 | 620 | return 0; |
| 577 | 621 | } |
| 578 | 622 | if( onoff ){ |
| 579 | 623 | int targetMode = (buf.st_mode & 0444)>>2; |
| 580 | 624 | if( (buf.st_mode & 0100)==0 ){ |
| 581 | 625 |
| --- src/file.c | |
| +++ src/file.c | |
| @@ -323,10 +323,51 @@ | |
| 323 | ** On Windows, always return False. |
| 324 | */ |
| 325 | int file_islink(const char *zFilename){ |
| 326 | return file_perm(zFilename, RepoFILE)==PERM_LNK; |
| 327 | } |
| 328 | |
| 329 | /* |
| 330 | ** Return 1 if zFilename is a directory. Return 0 if zFilename |
| 331 | ** does not exist. Return 2 if zFilename exists but is something |
| 332 | ** other than a directory. |
| @@ -570,11 +611,14 @@ | |
| 570 | */ |
| 571 | int file_setexe(const char *zFilename, int onoff){ |
| 572 | int rc = 0; |
| 573 | #if !defined(_WIN32) |
| 574 | struct stat buf; |
| 575 | if( fossil_stat(zFilename, &buf, RepoFILE)!=0 || S_ISLNK(buf.st_mode) ){ |
| 576 | return 0; |
| 577 | } |
| 578 | if( onoff ){ |
| 579 | int targetMode = (buf.st_mode & 0444)>>2; |
| 580 | if( (buf.st_mode & 0100)==0 ){ |
| 581 |
| --- src/file.c | |
| +++ src/file.c | |
| @@ -323,10 +323,51 @@ | |
| 323 | ** On Windows, always return False. |
| 324 | */ |
| 325 | int file_islink(const char *zFilename){ |
| 326 | return file_perm(zFilename, RepoFILE)==PERM_LNK; |
| 327 | } |
| 328 | |
| 329 | /* |
| 330 | ** Check every sub-directory of zRoot along the path to zFile. |
| 331 | ** If any sub-directory is really an ordinary file or a symbolic link, |
| 332 | ** then delete that object. The inputs are expected to be full |
| 333 | ** pathnames. |
| 334 | ** |
| 335 | ** Example: Given inputs |
| 336 | ** |
| 337 | ** zRoot = /home/alice/project1 |
| 338 | ** zFile = /home/alice/project1/main/src/js/fileA.js |
| 339 | ** |
| 340 | ** Look for objects in the following order: |
| 341 | ** |
| 342 | ** /home/alice/project/main |
| 343 | ** /home/alice/project/main/src |
| 344 | ** /home/alice/project/main/src/js |
| 345 | ** |
| 346 | ** If any of those objects exist and are something other than a directory |
| 347 | ** then delete the object and return. |
| 348 | */ |
| 349 | void file_delete_objects_on_path(const char *zRoot, const char *zFile){ |
| 350 | int i = (int)strlen(zRoot); |
| 351 | char *z = fossil_strdup(zFile); |
| 352 | assert( fossil_strnicmp(zRoot, z, i)==0 ); |
| 353 | if( i && zRoot[i-1]=='/' ) i--; |
| 354 | while( z[i]=='/' ){ |
| 355 | int j, rc; |
| 356 | for(j=i+1; z[j] && z[j]!='/'; j++){} |
| 357 | if( z[j]!='/' ) break; |
| 358 | z[j] = 0; |
| 359 | rc = file_isdir(z, SymFILE); |
| 360 | if( rc!=1 ){ |
| 361 | if( rc==2 ) file_delete(z); |
| 362 | break; |
| 363 | } |
| 364 | z[j] = '/'; |
| 365 | i = j; |
| 366 | } |
| 367 | fossil_free(z); |
| 368 | } |
| 369 | |
| 370 | /* |
| 371 | ** Return 1 if zFilename is a directory. Return 0 if zFilename |
| 372 | ** does not exist. Return 2 if zFilename exists but is something |
| 373 | ** other than a directory. |
| @@ -570,11 +611,14 @@ | |
| 611 | */ |
| 612 | int file_setexe(const char *zFilename, int onoff){ |
| 613 | int rc = 0; |
| 614 | #if !defined(_WIN32) |
| 615 | struct stat buf; |
| 616 | if( fossil_stat(zFilename, &buf, RepoFILE)!=0 |
| 617 | || S_ISLNK(buf.st_mode) |
| 618 | || S_ISDIR(buf.st_mode) |
| 619 | ){ |
| 620 | return 0; |
| 621 | } |
| 622 | if( onoff ){ |
| 623 | int targetMode = (buf.st_mode & 0444)>>2; |
| 624 | if( (buf.st_mode & 0100)==0 ){ |
| 625 |
+1
| --- src/vfile.c | ||
| +++ src/vfile.c | ||
| @@ -339,10 +339,11 @@ | ||
| 339 | 339 | blob_reset(&content); |
| 340 | 340 | continue; |
| 341 | 341 | } |
| 342 | 342 | } |
| 343 | 343 | if( verbose ) fossil_print("%s\n", &zName[nRepos]); |
| 344 | + file_delete_objects_on_path(g.zLocalRoot, zName); | |
| 344 | 345 | if( file_isdir(zName, RepoFILE)==1 ){ |
| 345 | 346 | /*TODO(dchest): remove directories? */ |
| 346 | 347 | fossil_fatal("%s is directory, cannot overwrite", zName); |
| 347 | 348 | } |
| 348 | 349 | if( file_size(zName, RepoFILE)>=0 && (isLink || file_islink(0)) ){ |
| 349 | 350 |
| --- src/vfile.c | |
| +++ src/vfile.c | |
| @@ -339,10 +339,11 @@ | |
| 339 | blob_reset(&content); |
| 340 | continue; |
| 341 | } |
| 342 | } |
| 343 | if( verbose ) fossil_print("%s\n", &zName[nRepos]); |
| 344 | if( file_isdir(zName, RepoFILE)==1 ){ |
| 345 | /*TODO(dchest): remove directories? */ |
| 346 | fossil_fatal("%s is directory, cannot overwrite", zName); |
| 347 | } |
| 348 | if( file_size(zName, RepoFILE)>=0 && (isLink || file_islink(0)) ){ |
| 349 |
| --- src/vfile.c | |
| +++ src/vfile.c | |
| @@ -339,10 +339,11 @@ | |
| 339 | blob_reset(&content); |
| 340 | continue; |
| 341 | } |
| 342 | } |
| 343 | if( verbose ) fossil_print("%s\n", &zName[nRepos]); |
| 344 | file_delete_objects_on_path(g.zLocalRoot, zName); |
| 345 | if( file_isdir(zName, RepoFILE)==1 ){ |
| 346 | /*TODO(dchest): remove directories? */ |
| 347 | fossil_fatal("%s is directory, cannot overwrite", zName); |
| 348 | } |
| 349 | if( file_size(zName, RepoFILE)>=0 && (isLink || file_islink(0)) ){ |
| 350 |