Fossil SCM
Improved error message and response when trying to manifest a check-out that contains a file beneath a symbolic link directory.
Commit
20d90dd4825ce5f7eb1168bdb62f75bed3447707bbb77f5720bda517e8bd4b76
Parent
3105bedff2deca0…
2 files changed
+11
-5
+7
-1
+11
-5
| --- src/file.c | ||
| +++ src/file.c | ||
| @@ -327,12 +327,13 @@ | ||
| 327 | 327 | } |
| 328 | 328 | |
| 329 | 329 | /* |
| 330 | 330 | ** Check every sub-directory of zRoot along the path to zFile. |
| 331 | 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. | |
| 332 | +** return an integer which is the length of the prefix of zFile which | |
| 333 | +** is the name of that object. Return 0 if all no non-directory | |
| 334 | +** objects are found along the path. | |
| 334 | 335 | ** |
| 335 | 336 | ** Example: Given inputs |
| 336 | 337 | ** |
| 337 | 338 | ** zRoot = /home/alice/project1 |
| 338 | 339 | ** zFile = /home/alice/project1/main/src/js/fileA.js |
| @@ -342,13 +343,14 @@ | ||
| 342 | 343 | ** /home/alice/project/main |
| 343 | 344 | ** /home/alice/project/main/src |
| 344 | 345 | ** /home/alice/project/main/src/js |
| 345 | 346 | ** |
| 346 | 347 | ** If any of those objects exist and are something other than a directory |
| 347 | -** then delete the object and return. | |
| 348 | +** then return the length of the name of the first non-directory object | |
| 349 | +** seen. | |
| 348 | 350 | */ |
| 349 | -void file_delete_objects_on_path(const char *zRoot, const char *zFile){ | |
| 351 | +int file_nondir_objects_on_path(const char *zRoot, const char *zFile){ | |
| 350 | 352 | int i = (int)strlen(zRoot); |
| 351 | 353 | char *z = fossil_strdup(zFile); |
| 352 | 354 | assert( fossil_strnicmp(zRoot, z, i)==0 ); |
| 353 | 355 | if( i && zRoot[i-1]=='/' ) i--; |
| 354 | 356 | while( z[i]=='/' ){ |
| @@ -356,17 +358,21 @@ | ||
| 356 | 358 | for(j=i+1; z[j] && z[j]!='/'; j++){} |
| 357 | 359 | if( z[j]!='/' ) break; |
| 358 | 360 | z[j] = 0; |
| 359 | 361 | rc = file_isdir(z, SymFILE); |
| 360 | 362 | if( rc!=1 ){ |
| 361 | - if( rc==2 ) file_delete(z); | |
| 363 | + if( rc==2 ){ | |
| 364 | + fossil_free(z); | |
| 365 | + return j; | |
| 366 | + } | |
| 362 | 367 | break; |
| 363 | 368 | } |
| 364 | 369 | z[j] = '/'; |
| 365 | 370 | i = j; |
| 366 | 371 | } |
| 367 | 372 | fossil_free(z); |
| 373 | + return 0; | |
| 368 | 374 | } |
| 369 | 375 | |
| 370 | 376 | /* |
| 371 | 377 | ** Return 1 if zFilename is a directory. Return 0 if zFilename |
| 372 | 378 | ** does not exist. Return 2 if zFilename exists but is something |
| 373 | 379 |
| --- src/file.c | |
| +++ src/file.c | |
| @@ -327,12 +327,13 @@ | |
| 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 |
| @@ -342,13 +343,14 @@ | |
| 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]=='/' ){ |
| @@ -356,17 +358,21 @@ | |
| 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 |
| --- src/file.c | |
| +++ src/file.c | |
| @@ -327,12 +327,13 @@ | |
| 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 | ** return an integer which is the length of the prefix of zFile which |
| 333 | ** is the name of that object. Return 0 if all no non-directory |
| 334 | ** objects are found along the path. |
| 335 | ** |
| 336 | ** Example: Given inputs |
| 337 | ** |
| 338 | ** zRoot = /home/alice/project1 |
| 339 | ** zFile = /home/alice/project1/main/src/js/fileA.js |
| @@ -342,13 +343,14 @@ | |
| 343 | ** /home/alice/project/main |
| 344 | ** /home/alice/project/main/src |
| 345 | ** /home/alice/project/main/src/js |
| 346 | ** |
| 347 | ** If any of those objects exist and are something other than a directory |
| 348 | ** then return the length of the name of the first non-directory object |
| 349 | ** seen. |
| 350 | */ |
| 351 | int file_nondir_objects_on_path(const char *zRoot, const char *zFile){ |
| 352 | int i = (int)strlen(zRoot); |
| 353 | char *z = fossil_strdup(zFile); |
| 354 | assert( fossil_strnicmp(zRoot, z, i)==0 ); |
| 355 | if( i && zRoot[i-1]=='/' ) i--; |
| 356 | while( z[i]=='/' ){ |
| @@ -356,17 +358,21 @@ | |
| 358 | for(j=i+1; z[j] && z[j]!='/'; j++){} |
| 359 | if( z[j]!='/' ) break; |
| 360 | z[j] = 0; |
| 361 | rc = file_isdir(z, SymFILE); |
| 362 | if( rc!=1 ){ |
| 363 | if( rc==2 ){ |
| 364 | fossil_free(z); |
| 365 | return j; |
| 366 | } |
| 367 | break; |
| 368 | } |
| 369 | z[j] = '/'; |
| 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 |
+7
-1
| --- src/vfile.c | ||
| +++ src/vfile.c | ||
| @@ -307,10 +307,11 @@ | ||
| 307 | 307 | g.zLocalRoot, id); |
| 308 | 308 | } |
| 309 | 309 | while( db_step(&q)==SQLITE_ROW ){ |
| 310 | 310 | int id, rid, isExe, isLink; |
| 311 | 311 | const char *zName; |
| 312 | + int n; | |
| 312 | 313 | |
| 313 | 314 | id = db_column_int(&q, 0); |
| 314 | 315 | zName = db_column_text(&q, 1); |
| 315 | 316 | rid = db_column_int(&q, 2); |
| 316 | 317 | isExe = db_column_int(&q, 3); |
| @@ -339,11 +340,16 @@ | ||
| 339 | 340 | blob_reset(&content); |
| 340 | 341 | continue; |
| 341 | 342 | } |
| 342 | 343 | } |
| 343 | 344 | if( verbose ) fossil_print("%s\n", &zName[nRepos]); |
| 344 | - file_delete_objects_on_path(g.zLocalRoot, zName); | |
| 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 | + } | |
| 345 | 351 | if( file_isdir(zName, RepoFILE)==1 ){ |
| 346 | 352 | /*TODO(dchest): remove directories? */ |
| 347 | 353 | fossil_fatal("%s is directory, cannot overwrite", zName); |
| 348 | 354 | } |
| 349 | 355 | if( file_size(zName, RepoFILE)>=0 && (isLink || file_islink(0)) ){ |
| 350 | 356 |
| --- src/vfile.c | |
| +++ src/vfile.c | |
| @@ -307,10 +307,11 @@ | |
| 307 | g.zLocalRoot, id); |
| 308 | } |
| 309 | while( db_step(&q)==SQLITE_ROW ){ |
| 310 | int id, rid, isExe, isLink; |
| 311 | const char *zName; |
| 312 | |
| 313 | id = db_column_int(&q, 0); |
| 314 | zName = db_column_text(&q, 1); |
| 315 | rid = db_column_int(&q, 2); |
| 316 | isExe = db_column_int(&q, 3); |
| @@ -339,11 +340,16 @@ | |
| 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 |
| --- src/vfile.c | |
| +++ src/vfile.c | |
| @@ -307,10 +307,11 @@ | |
| 307 | g.zLocalRoot, id); |
| 308 | } |
| 309 | while( db_step(&q)==SQLITE_ROW ){ |
| 310 | int id, rid, isExe, isLink; |
| 311 | const char *zName; |
| 312 | int n; |
| 313 | |
| 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); |
| @@ -339,11 +340,16 @@ | |
| 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 |