Fossil

Check-in [fe1264d35d]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Cherrypick key fixes from the sec2020 branch in order to devise a minimal patch to get us to version 2.12.1.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | sec2020-2.12-patch
Files: files | file ages | folders
SHA3-256: fe1264d35df2fe16a7ac3c3fbc7d607718319ce148adb9961ff05e9df2a92158
User & Date: drh 2020-08-19 01:07:57.238
Context
2020-08-19
12:58
Merge additional symlink fixes. Back out comment-only changes from url.c. check-in: 0ea17c2b11 user: drh tags: sec2020-2.12-patch
01:07
Cherrypick key fixes from the sec2020 branch in order to devise a minimal patch to get us to version 2.12.1. check-in: fe1264d35d user: drh tags: sec2020-2.12-patch
00:15
Do not allow the "fossil add" command to add files beneath a symlink. check-in: a6abfb911b user: drh tags: sec2020
2020-08-18
23:39
An alternative method for dealing with historical files named "_FOSSIL_" in the tree: Simply pretend they are not there. Closed-Leaf check-in: 8f24c07917 user: drh tags: ignore-reserved-filenames
21:03
Cherrypick [d2d8894bb2]: fossil.storage.clear() is now also sandboxed - no longer nuking all state for all repos on the same origin. check-in: d0988e677c user: stephan tags: branch-2.12
20:58
Silently refuse to "fossil add" files that use reserved names. check-in: 888da94e0a user: drh tags: sec2020
20:19
Improved error message and response when trying to manifest a check-out that contains a file beneath a symbolic link directory. check-in: 20d90dd482 user: drh tags: sec2020
12:17
When writing files to disk for a check-out, refuse to write through a symbolic link to a directory. Ticket [f9831fdef1d4edcc]. check-in: a64e384f0c user: drh tags: sec2020
2020-08-17
22:22
Fixes for reserved names case sensitivity, coding style adjustments, more tests. check-in: fde20bc03c user: mistachkin tags: sec2020
18:20
Merge in reject-ckout-db branch. check-in: 8c16884aa2 user: stephan tags: sec2020
14:09
Set an authorizer when running the ticket-table SQL. Ticket [56b82836ffba9952]. check-in: fb41384045 user: drh tags: sec2020
09:16
Prohibit redirects from HTTP or HTTPS over to SSH or FILE. Fix for ticket [61613b0a9cf843b6]. check-in: 253dbd15e2 user: drh tags: sec2020
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/add.c.
184
185
186
187
188
189
190
191


192
193
194
195
196
197
198
    return 0;
  }
}

/*
** Add all files in the sfile temp table.
**
** Automatically exclude the repository file.


*/
static int add_files_in_sfile(int vid){
  const char *zRepo;        /* Name of the repository database file */
  int nAdd = 0;             /* Number of files added */
  int i;                    /* Loop counter */
  const char *zReserved;    /* Name of a reserved file */
  Blob repoName;            /* Treename of the repository */







|
>
>







184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
    return 0;
  }
}

/*
** Add all files in the sfile temp table.
**
** Automatically exclude the repository file and any other files
** with reserved names. Also exclude files that are beneath an 
** existing symlink.
*/
static int add_files_in_sfile(int vid){
  const char *zRepo;        /* Name of the repository database file */
  int nAdd = 0;             /* Number of files added */
  int i;                    /* Loop counter */
  const char *zReserved;    /* Name of a reserved file */
  Blob repoName;            /* Treename of the repository */
206
207
208
209
210
211
212
213








214
215
216



217
218
219
220

221
222
223
224
225
226
227
    zRepo = blob_str(&repoName);
  }
  if( filenames_are_case_sensitive() ){
    xCmp = fossil_strcmp;
  }else{
    xCmp = fossil_stricmp;
  }
  db_prepare(&loop, "SELECT pathname FROM sfile ORDER BY pathname");








  while( db_step(&loop)==SQLITE_ROW ){
    const char *zToAdd = db_column_text(&loop, 0);
    if( fossil_strcmp(zToAdd, zRepo)==0 ) continue;



    for(i=0; (zReserved = fossil_reserved_name(i, 0))!=0; i++){
      if( xCmp(zToAdd, zReserved)==0 ) break;
    }
    if( zReserved ) continue;

    nAdd += add_one_file(zToAdd, vid);
  }
  db_finalize(&loop);
  blob_reset(&repoName);
  return nAdd;
}








|
>
>
>
>
>
>
>
>



>
>
>
|
|
|
|
>







208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
    zRepo = blob_str(&repoName);
  }
  if( filenames_are_case_sensitive() ){
    xCmp = fossil_strcmp;
  }else{
    xCmp = fossil_stricmp;
  }
  db_prepare(&loop, 
     "SELECT pathname FROM sfile"
     " WHERE pathname NOT IN ("
       "SELECT sfile.pathname FROM vfile, sfile"
       " WHERE vfile.islink"
       "   AND NOT vfile.deleted"
       "   AND sfile.pathname>(vfile.pathname||'/')"
       "   AND sfile.pathname<(vfile.pathname||'0'))"
     " ORDER BY pathname");
  while( db_step(&loop)==SQLITE_ROW ){
    const char *zToAdd = db_column_text(&loop, 0);
    if( fossil_strcmp(zToAdd, zRepo)==0 ) continue;
    if( strchr(zToAdd,'/') ){
      if( file_is_reserved_name(zToAdd, -1) ) continue;
    }else{
      for(i=0; (zReserved = fossil_reserved_name(i, 0))!=0; i++){
        if( xCmp(zToAdd, zReserved)==0 ) break;
      }
      if( zReserved ) continue;
    }
    nAdd += add_one_file(zToAdd, vid);
  }
  db_finalize(&loop);
  blob_reset(&repoName);
  return nAdd;
}

Changes to src/db.c.
128
129
130
131
132
133
134



135
136
137
138
139
140
141
  } aHook[5];
  char *azDeleteOnFail[3];  /* Files to delete on a failure */
  char *azBeforeCommit[5];  /* Commands to run prior to COMMIT */
  int nBeforeCommit;        /* Number of entries in azBeforeCommit */
  int nPriorChanges;        /* sqlite3_total_changes() at transaction start */
  const char *zStartFile;   /* File in which transaction was started */
  int iStartLine;           /* Line of zStartFile where transaction started */



} db = {0, 0, 0, 0, 0, 0, };

/*
** Arrange for the given file to be deleted on a failure.
*/
void db_delete_on_failure(const char *zFilename){
  assert( db.nDeleteOnFail<count(db.azDeleteOnFail) );







>
>
>







128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
  } aHook[5];
  char *azDeleteOnFail[3];  /* Files to delete on a failure */
  char *azBeforeCommit[5];  /* Commands to run prior to COMMIT */
  int nBeforeCommit;        /* Number of entries in azBeforeCommit */
  int nPriorChanges;        /* sqlite3_total_changes() at transaction start */
  const char *zStartFile;   /* File in which transaction was started */
  int iStartLine;           /* Line of zStartFile where transaction started */
  int (*xAuth)(void*,int,const char*,const char*,const char*,const char*);
  void *pAuthArg;           /* Argument to the authorizer */
  const char *zAuthName;    /* Name of the authorizer */
} db = {0, 0, 0, 0, 0, 0, };

/*
** Arrange for the given file to be deleted on a failure.
*/
void db_delete_on_failure(const char *zFilename){
  assert( db.nDeleteOnFail<count(db.azDeleteOnFail) );
314
315
316
317
318
319
320


























321
322
323
324
325
326
327
      db.aHook[i].xHook = xS;
    }
  }
  db.aHook[db.nCommitHook].sequence = sequence;
  db.aHook[db.nCommitHook].xHook = x;
  db.nCommitHook++;
}



























#if INTERFACE
/*
** Possible flags to db_vprepare
*/
#define DB_PREPARE_IGNORE_ERROR  0x001  /* Suppress errors */
#define DB_PREPARE_PERSISTENT    0x002  /* Stmt will stick around for a while */







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
      db.aHook[i].xHook = xS;
    }
  }
  db.aHook[db.nCommitHook].sequence = sequence;
  db.aHook[db.nCommitHook].xHook = x;
  db.nCommitHook++;
}

/*
** Set or unset the query authorizer callback function
*/
void db_set_authorizer(
  int(*xAuth)(void*,int,const char*,const char*,const char*,const char*),
  void *pArg,
  const char *zName /* for tracing */
){
  if( db.xAuth ){
    fossil_panic("multiple active db_set_authorizer() calls");
  }
  if( g.db ) sqlite3_set_authorizer(g.db, xAuth, pArg);
  db.xAuth = xAuth;
  db.pAuthArg = pArg;
  db.zAuthName = zName;
  if( g.fSqlTrace ) fossil_trace("-- set authorizer %s\n", zName);
}
void db_clear_authorizer(void){
  if( db.zAuthName && g.fSqlTrace ){
    fossil_trace("-- discontinue authorizer %s\n", db.zAuthName);
  }
  if( g.db ) sqlite3_set_authorizer(g.db, 0, 0);
  db.xAuth = 0;
  db.pAuthArg = 0;
}

#if INTERFACE
/*
** Possible flags to db_vprepare
*/
#define DB_PREPARE_IGNORE_ERROR  0x001  /* Suppress errors */
#define DB_PREPARE_PERSISTENT    0x002  /* Stmt will stick around for a while */
842
843
844
845
846
847
848
849
850
851
852
853
854
855



856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
** database.
*/
void db_init_database(
  const char *zFileName,   /* Name of database file to create */
  const char *zSchema,     /* First part of schema */
  ...                      /* Additional SQL to run.  Terminate with NULL. */
){
  sqlite3 *db;
  int rc;
  const char *zSql;
  va_list ap;

  db = db_open(zFileName ? zFileName : ":memory:");
  sqlite3_exec(db, "BEGIN EXCLUSIVE", 0, 0, 0);



  rc = sqlite3_exec(db, zSchema, 0, 0, 0);
  if( rc!=SQLITE_OK ){
    db_err("%s", sqlite3_errmsg(db));
  }
  va_start(ap, zSchema);
  while( (zSql = va_arg(ap, const char*))!=0 ){
    rc = sqlite3_exec(db, zSql, 0, 0, 0);
    if( rc!=SQLITE_OK ){
      db_err("%s", sqlite3_errmsg(db));
    }
  }
  va_end(ap);
  sqlite3_exec(db, "COMMIT", 0, 0, 0);
  if( zFileName || g.db!=0 ){
    sqlite3_close(db);
  }else{
    g.db = db;
  }
}

/*
** Function to return the number of seconds since 1970.  This is
** the same as strftime('%s','now') but is more compact.
*/







|




|
|
>
>
>
|

|



|

|



|

|

|







871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
** database.
*/
void db_init_database(
  const char *zFileName,   /* Name of database file to create */
  const char *zSchema,     /* First part of schema */
  ...                      /* Additional SQL to run.  Terminate with NULL. */
){
  sqlite3 *xdb;
  int rc;
  const char *zSql;
  va_list ap;

  xdb = db_open(zFileName ? zFileName : ":memory:");
  sqlite3_exec(xdb, "BEGIN EXCLUSIVE", 0, 0, 0);
  if( db.xAuth ){
    sqlite3_set_authorizer(xdb, db.xAuth, db.pAuthArg);
  }
  rc = sqlite3_exec(xdb, zSchema, 0, 0, 0);
  if( rc!=SQLITE_OK ){
    db_err("%s", sqlite3_errmsg(xdb));
  }
  va_start(ap, zSchema);
  while( (zSql = va_arg(ap, const char*))!=0 ){
    rc = sqlite3_exec(xdb, zSql, 0, 0, 0);
    if( rc!=SQLITE_OK ){
      db_err("%s", sqlite3_errmsg(xdb));
    }
  }
  va_end(ap);
  sqlite3_exec(xdb, "COMMIT", 0, 0, 0);
  if( zFileName || g.db!=0 ){
    sqlite3_close(xdb);
  }else{
    g.db = xdb;
  }
}

/*
** Function to return the number of seconds since 1970.  This is
** the same as strftime('%s','now') but is more compact.
*/
2084
2085
2086
2087
2088
2089
2090

2091
2092
2093
2094
2095
2096
2097
**
** Check for unfinalized statements and report errors if the reportErrors
** argument is true.  Ignore unfinalized statements when false.
*/
void db_close(int reportErrors){
  sqlite3_stmt *pStmt;
  if( g.db==0 ) return;

  if( g.fSqlStats ){
    int cur, hiwtr;
    sqlite3_db_status(g.db, SQLITE_DBSTATUS_LOOKASIDE_USED, &cur, &hiwtr, 0);
    fprintf(stderr, "-- LOOKASIDE_USED         %10d %10d\n", cur, hiwtr);
    sqlite3_db_status(g.db, SQLITE_DBSTATUS_LOOKASIDE_HIT, &cur, &hiwtr, 0);
    fprintf(stderr, "-- LOOKASIDE_HIT                     %10d\n", hiwtr);
    sqlite3_db_status(g.db, SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE, &cur,&hiwtr,0);







>







2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
**
** Check for unfinalized statements and report errors if the reportErrors
** argument is true.  Ignore unfinalized statements when false.
*/
void db_close(int reportErrors){
  sqlite3_stmt *pStmt;
  if( g.db==0 ) return;
  sqlite3_set_authorizer(g.db, 0, 0);
  if( g.fSqlStats ){
    int cur, hiwtr;
    sqlite3_db_status(g.db, SQLITE_DBSTATUS_LOOKASIDE_USED, &cur, &hiwtr, 0);
    fprintf(stderr, "-- LOOKASIDE_USED         %10d %10d\n", cur, hiwtr);
    sqlite3_db_status(g.db, SQLITE_DBSTATUS_LOOKASIDE_HIT, &cur, &hiwtr, 0);
    fprintf(stderr, "-- LOOKASIDE_HIT                     %10d\n", hiwtr);
    sqlite3_db_status(g.db, SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE, &cur,&hiwtr,0);
2113
2114
2115
2116
2117
2118
2119
2120

2121
2122

2123
2124
2125

2126
2127
2128
2129
2130
2131
2132
2133
    sqlite3_status(SQLITE_STATUS_PAGECACHE_OVERFLOW, &cur, &hiwtr, 0);
    fprintf(stderr, "-- PCACHE_OVFLOW          %10d %10d\n", cur, hiwtr);
    fprintf(stderr, "-- prepared statements    %10d\n", db.nPrepare);
  }
  while( db.pAllStmt ){
    db_finalize(db.pAllStmt);
  }
  if( db.nBegin && reportErrors ){

    fossil_warning("Transaction started at %s:%d never commits",
                   db.zStartFile, db.iStartLine);

    db_end_transaction(1);
  }
  pStmt = 0;

  g.dbIgnoreErrors++; /* Stop "database locked" warnings from PRAGMA optimize */
  sqlite3_exec(g.db, "PRAGMA optimize", 0, 0, 0);
  g.dbIgnoreErrors--;
  db_close_config();

  /* If the localdb has a lot of unused free space,
  ** then VACUUM it as we shut down.
  */







|
>
|
|
>



>
|







2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
    sqlite3_status(SQLITE_STATUS_PAGECACHE_OVERFLOW, &cur, &hiwtr, 0);
    fprintf(stderr, "-- PCACHE_OVFLOW          %10d %10d\n", cur, hiwtr);
    fprintf(stderr, "-- prepared statements    %10d\n", db.nPrepare);
  }
  while( db.pAllStmt ){
    db_finalize(db.pAllStmt);
  }
  if( db.nBegin ){
    if( reportErrors ){
      fossil_warning("Transaction started at %s:%d never commits",
                     db.zStartFile, db.iStartLine);
    }
    db_end_transaction(1);
  }
  pStmt = 0;
  sqlite3_busy_timeout(g.db, 0);
  g.dbIgnoreErrors++; /* Stop "database locked" warnings */
  sqlite3_exec(g.db, "PRAGMA optimize", 0, 0, 0);
  g.dbIgnoreErrors--;
  db_close_config();

  /* If the localdb has a lot of unused free space,
  ** then VACUUM it as we shut down.
  */
2163
2164
2165
2166
2167
2168
2169

2170
2171
2172
2173
2174
2175
2176
*/
void db_panic_close(void){
  if( g.db ){
    int rc;
    sqlite3_wal_checkpoint(g.db, 0);
    rc = sqlite3_close(g.db);
    if( g.fSqlTrace ) fossil_trace("-- sqlite3_close(%d)\n", rc);

  }
  g.db = 0;
  g.repositoryOpen = 0;
  g.localOpen = 0;
}

/*







>







2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
*/
void db_panic_close(void){
  if( g.db ){
    int rc;
    sqlite3_wal_checkpoint(g.db, 0);
    rc = sqlite3_close(g.db);
    if( g.fSqlTrace ) fossil_trace("-- sqlite3_close(%d)\n", rc);
    db_clear_authorizer();
  }
  g.db = 0;
  g.repositoryOpen = 0;
  g.localOpen = 0;
}

/*
Changes to src/file.c.
321
322
323
324
325
326
327















































328
329
330
331
332
333
334
** This routines RepoFILE - that zFilename is always a file under management.
**
** On Windows, always return False.
*/
int file_islink(const char *zFilename){
  return file_perm(zFilename, RepoFILE)==PERM_LNK;
}
















































/*
** Return 1 if zFilename is a directory.  Return 0 if zFilename
** does not exist.  Return 2 if zFilename exists but is something
** other than a directory.
*/
int file_isdir(const char *zFilename, int eFType){







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
** This routines RepoFILE - that zFilename is always a file under management.
**
** On Windows, always return False.
*/
int file_islink(const char *zFilename){
  return file_perm(zFilename, RepoFILE)==PERM_LNK;
}

/*
** Check every sub-directory of zRoot along the path to zFile.
** If any sub-directory is really an ordinary file or a symbolic link,
** return an integer which is the length of the prefix of zFile which
** is the name of that object.  Return 0 if all no non-directory
** objects are found along the path.
**
** Example:  Given inputs
**
**     zRoot = /home/alice/project1
**     zFile = /home/alice/project1/main/src/js/fileA.js
**
** Look for objects in the following order:
**
**      /home/alice/project/main
**      /home/alice/project/main/src
**      /home/alice/project/main/src/js
**
** If any of those objects exist and are something other than a directory
** then return the length of the name of the first non-directory object
** seen.
*/
int file_nondir_objects_on_path(const char *zRoot, const char *zFile){
  int i = (int)strlen(zRoot);
  char *z = fossil_strdup(zFile);
  assert( fossil_strnicmp(zRoot, z, i)==0 );
  if( i && zRoot[i-1]=='/' ) i--;
  while( z[i]=='/' ){
    int j, rc;
    for(j=i+1; z[j] && z[j]!='/'; j++){}
    if( z[j]!='/' ) break;
    z[j] = 0;
    rc = file_isdir(z, SymFILE);
    if( rc!=1 ){
      if( rc==2 ){
        fossil_free(z);
        return j;
      }
      break;
    }
    z[j] = '/';
    i = j;
  }
  fossil_free(z);
  return 0;
}

/*
** Return 1 if zFilename is a directory.  Return 0 if zFilename
** does not exist.  Return 2 if zFilename exists but is something
** other than a directory.
*/
int file_isdir(const char *zFilename, int eFType){
568
569
570
571
572
573
574
575



576
577
578
579
580
581
582
** zFilename is a symbolic link, it is the object that zFilename points
** to that is modified.
*/
int file_setexe(const char *zFilename, int onoff){
  int rc = 0;
#if !defined(_WIN32)
  struct stat buf;
  if( fossil_stat(zFilename, &buf, RepoFILE)!=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;







|
>
>
>







615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
** zFilename is a symbolic link, it is the object that zFilename points
** to that is modified.
*/
int file_setexe(const char *zFilename, int onoff){
  int rc = 0;
#if !defined(_WIN32)
  struct stat buf;
  if( fossil_stat(zFilename, &buf, RepoFILE)!=0 
   || S_ISLNK(buf.st_mode)
   || S_ISDIR(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;
2395
2396
2397
2398
2399
2400
2401



















































































  if( dryRunFlag!=0 ){
    fossil_print("dry-run: would have touched %d file(s)\n",
                 changeCount);
  }else{
    fossil_print("Touched %d file(s)\n", changeCount);
  }
}


























































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
  if( dryRunFlag!=0 ){
    fossil_print("dry-run: would have touched %d file(s)\n",
                 changeCount);
  }else{
    fossil_print("Touched %d file(s)\n", changeCount);
  }
}

/*
** Returns non-zero if the specified file name ends with any reserved name,
** e.g.: _FOSSIL_ or .fslckout.  Specifically, it returns 1 for exact match
** or 2 for a tail match on a longer file name.
**
** For the sake of efficiency, zFilename must be a canonical name, e.g. an
** absolute path using only forward slash ('/') as a directory separator.
**
** nFilename must be the length of zFilename.  When negative, strlen() will
** be used to calculate it.
*/
int file_is_reserved_name(const char *zFilename, int nFilename){
  const char *zEnd;  /* one-after-the-end of zFilename */
  int gotSuffix = 0; /* length of suffix (-wal, -shm, -journal) */

  assert( zFilename && "API misuse" );
  if( nFilename<0 ) nFilename = (int)strlen(zFilename);
  if( nFilename<8 ) return 0; /* strlen("_FOSSIL_") */
  zEnd = zFilename + nFilename;
  if( nFilename>=12 ){ /* strlen("_FOSSIL_-(shm|wal)") */
    /* Check for (-wal, -shm, -journal) suffixes, with an eye towards
    ** runtime speed. */
    if( zEnd[-4]=='-' ){
      if( fossil_strnicmp("wal", &zEnd[-3], 3)
       && fossil_strnicmp("shm", &zEnd[-3], 3) ){
        return 0;
      }
      gotSuffix = 4;
    }else if( nFilename>=16 && zEnd[-8]=='-' ){ /*strlen(_FOSSIL_-journal) */
      if( fossil_strnicmp("journal", &zEnd[-7], 7) ) return 0;
      gotSuffix = 8;
    }
    if( gotSuffix ){
      assert( 4==gotSuffix || 8==gotSuffix );
      zEnd -= gotSuffix;
      nFilename -= gotSuffix;
      gotSuffix = 1;
    }
    assert( nFilename>=8 && "strlen(_FOSSIL_)" );
    assert( gotSuffix==0 || gotSuffix==1 );
  }
  switch( zEnd[-1] ){
    case '_':{
      if( fossil_strnicmp("_FOSSIL_", &zEnd[-8], 8) ) return 0;
      if( 8==nFilename ) return 1;
      return zEnd[-9]=='/' ? 2 : gotSuffix;
    }
    case 'T':
    case 't':{
      if( nFilename<9 || zEnd[-9]!='.'
       || fossil_strnicmp(".fslckout", &zEnd[-9], 9) ){
        return 0; 
      }
      if( 9==nFilename ) return 1;
      return zEnd[-10]=='/' ? 2 : gotSuffix;
    }
    default:{
      return 0;
    }
  }
}

/*
** COMMAND: test-is-reserved-name
**
** Usage: %fossil test-is-ckout-db FILENAMES...
**
** Passes each given name to file_is_reserved_name() and outputs one
** line per file: the result value of that function followed by the
** name.
*/
void test_is_reserved_name_cmd(void){
  int i;

  if(g.argc<3){
    usage("FILENAME_1 [...FILENAME_N]");
  }
  for( i = 2; i < g.argc; ++i ){
    const int check = file_is_reserved_name(g.argv[i], -1);
    fossil_print("%d %s\n", check, g.argv[i]);
  }
}
Changes to src/http.c.
373
374
375
376
377
378
379





380
381
382
383
384
385
386




387
388
389
390
391
392
393
      j = strlen(zLine) - 1;
      while( j>4 && fossil_strcmp(&zLine[j-4],"/xfer")==0 ){
         j -= 4;
         zLine[j] = 0;
      }
      if( (mHttpFlags & HTTP_QUIET)==0 ){
        fossil_print("redirect with status %d to %s\n", rc, &zLine[i]);





      }
      wasHttps = g.url.isHttps;
      url_parse(&zLine[i], 0);
      if( wasHttps && !g.url.isHttps ){
        fossil_warning("cannot redirect from HTTPS to HTTP");
        goto write_err;
       }




      transport_close(&g.url);
      transport_global_shutdown(&g.url);
      fSeenHttpAuth = 0;
      if( g.zHttpAuth ) free(g.zHttpAuth);
      g.zHttpAuth = get_httpauth();
      if( rc==301 || rc==308 ) url_remember();
      return http_exchange(pSend, pReply, mHttpFlags,







>
>
>
>
>






|
>
>
>
>







373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
      j = strlen(zLine) - 1;
      while( j>4 && fossil_strcmp(&zLine[j-4],"/xfer")==0 ){
         j -= 4;
         zLine[j] = 0;
      }
      if( (mHttpFlags & HTTP_QUIET)==0 ){
        fossil_print("redirect with status %d to %s\n", rc, &zLine[i]);
      }
      if( g.url.isFile || g.url.isSsh ){
        fossil_warning("cannot redirect from %s to %s", g.url.canonical,
                       &zLine[i]);
        goto write_err;
      }
      wasHttps = g.url.isHttps;
      url_parse(&zLine[i], 0);
      if( wasHttps && !g.url.isHttps ){
        fossil_warning("cannot redirect from HTTPS to HTTP");
        goto write_err;
      }
      if( g.url.isSsh || g.url.isFile ){
        fossil_warning("cannot redirect to %s", &zLine[i]);
        goto write_err;
      }
      transport_close(&g.url);
      transport_global_shutdown(&g.url);
      fSeenHttpAuth = 0;
      if( g.zHttpAuth ) free(g.zHttpAuth);
      g.zHttpAuth = get_httpauth();
      if( rc==301 || rc==308 ) url_remember();
      return http_exchange(pSend, pReply, mHttpFlags,
Changes to src/manifest.c.
479
480
481
482
483
484
485



486
487
488
489






490
491
492
493
494
495
496
  if( n<10 || z[0]<'A' || z[0]>'Z' || z[1]!=' ' ){
    blob_reset(pContent);
    blob_appendf(pErr, "line 1 not recognized");
    return 0;
  }
  /* Then verify the Z-card.
  */



  if( verify_z_card(z, n, pErr)==2 ){
    blob_reset(pContent);
    return 0;
  }







  /* Allocate a Manifest object to hold the parsed control artifact.
  */
  p = fossil_malloc( sizeof(*p) );
  memset(p, 0, sizeof(*p));
  memcpy(&p->content, pContent, sizeof(p->content));
  p->rid = rid;







>
>
>




>
>
>
>
>
>







479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
  if( n<10 || z[0]<'A' || z[0]>'Z' || z[1]!=' ' ){
    blob_reset(pContent);
    blob_appendf(pErr, "line 1 not recognized");
    return 0;
  }
  /* Then verify the Z-card.
  */
#if 1
  /* Disable this ***ONLY*** (ONLY!) when testing hand-written inputs
     for card-related syntax errors. */
  if( verify_z_card(z, n, pErr)==2 ){
    blob_reset(pContent);
    return 0;
  }
#else
#warning ACHTUNG - z-card check is disabled for testing purposes.
  if(0 && verify_z_card(NULL, 0, NULL)){
    /*avoid unused static func error*/
  }
#endif

  /* Allocate a Manifest object to hold the parsed control artifact.
  */
  p = fossil_malloc( sizeof(*p) );
  memset(p, 0, sizeof(*p));
  memcpy(&p->content, pContent, sizeof(p->content));
  p->rid = rid;
599
600
601
602
603
604
605

606
607
608
609
610
611
612
      ** is when the specific event is said to occur.
      */
      case 'E': {
        if( p->rEventDate>0.0 ) SYNTAX("more than one E-card");
        p->rEventDate = db_double(0.0,"SELECT julianday(%Q)", next_token(&x,0));
        if( p->rEventDate<=0.0 ) SYNTAX("malformed date on E-card");
        p->zEventId = next_token(&x, &sz);

        if( !hname_validate(p->zEventId, sz) ){
          SYNTAX("malformed hash on E-card");
        }
        p->type = CFTYPE_EVENT;
        break;
      }








>







608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
      ** is when the specific event is said to occur.
      */
      case 'E': {
        if( p->rEventDate>0.0 ) SYNTAX("more than one E-card");
        p->rEventDate = db_double(0.0,"SELECT julianday(%Q)", next_token(&x,0));
        if( p->rEventDate<=0.0 ) SYNTAX("malformed date on E-card");
        p->zEventId = next_token(&x, &sz);
        if( p->zEventId==0 ) SYNTAX("missing hash on E-card");
        if( !hname_validate(p->zEventId, sz) ){
          SYNTAX("malformed hash on E-card");
        }
        p->type = CFTYPE_EVENT;
        break;
      }

623
624
625
626
627
628
629

630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647










648
649
650
651
652
653
654
655
656
657
658
659
660
661
        if( zName==0 ) SYNTAX("missing filename on F-card");
        defossilize(zName);
        if( !file_is_simple_pathname_nonstrict(zName) ){
          SYNTAX("F-card filename is not a simple path");
        }
        zUuid = next_token(&x, &sz);
        if( p->zBaseline==0 || zUuid!=0 ){

          if( !hname_validate(zUuid,sz) ){
            SYNTAX("F-card hash invalid");
          }
        }
        zPerm = next_token(&x,0);
        zPriorName = next_token(&x,0);
        if( zPriorName ){
          defossilize(zPriorName);
          if( !file_is_simple_pathname_nonstrict(zPriorName) ){
            SYNTAX("F-card old filename is not a simple path");
          }
        }
        if( p->nFile>=p->nFileAlloc ){
          p->nFileAlloc = p->nFileAlloc*2 + 10;
          p->aFile = fossil_realloc(p->aFile,
                                    p->nFileAlloc*sizeof(p->aFile[0]) );
        }
        i = p->nFile++;










        p->aFile[i].zName = zName;
        p->aFile[i].zUuid = zUuid;
        p->aFile[i].zPerm = zPerm;
        p->aFile[i].zPrior = zPriorName;
        if( i>0 && fossil_strcmp(p->aFile[i-1].zName, zName)>=0 ){
          SYNTAX("incorrect F-card sort order");
        }
        p->type = CFTYPE_MANIFEST;
        break;
      }

      /*
      **    G <hash>
      **







>


















>
>
>
>
>
>
>
>
>
>




<
<
<







633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672



673
674
675
676
677
678
679
        if( zName==0 ) SYNTAX("missing filename on F-card");
        defossilize(zName);
        if( !file_is_simple_pathname_nonstrict(zName) ){
          SYNTAX("F-card filename is not a simple path");
        }
        zUuid = next_token(&x, &sz);
        if( p->zBaseline==0 || zUuid!=0 ){
          if( zUuid==0 ) SYNTAX("missing hash on F-card");
          if( !hname_validate(zUuid,sz) ){
            SYNTAX("F-card hash invalid");
          }
        }
        zPerm = next_token(&x,0);
        zPriorName = next_token(&x,0);
        if( zPriorName ){
          defossilize(zPriorName);
          if( !file_is_simple_pathname_nonstrict(zPriorName) ){
            SYNTAX("F-card old filename is not a simple path");
          }
        }
        if( p->nFile>=p->nFileAlloc ){
          p->nFileAlloc = p->nFileAlloc*2 + 10;
          p->aFile = fossil_realloc(p->aFile,
                                    p->nFileAlloc*sizeof(p->aFile[0]) );
        }
        i = p->nFile++;
        if( i>0 && fossil_strcmp(p->aFile[i-1].zName, zName)>=0 ){
          SYNTAX("incorrect F-card sort order");
        }
        if( file_is_reserved_name(zName,-1) ){
          /* If reserved names leaked into historical manifests due to
          ** slack oversight by older versions of Fossil, simply ignore
          ** those files */
          p->nFile--;
          break;
        }
        p->aFile[i].zName = zName;
        p->aFile[i].zUuid = zUuid;
        p->aFile[i].zPerm = zPerm;
        p->aFile[i].zPrior = zPriorName;



        p->type = CFTYPE_MANIFEST;
        break;
      }

      /*
      **    G <hash>
      **
Changes to src/report.c.
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
  return rc;
}

/*
** Activate the query authorizer
*/
void report_restrict_sql(char **pzErr){
  sqlite3_set_authorizer(g.db, report_query_authorizer, (void*)pzErr);
  sqlite3_limit(g.db, SQLITE_LIMIT_VDBE_OP, 10000);
}
void report_unrestrict_sql(void){
  sqlite3_set_authorizer(g.db, 0, 0);
}


/*
** Check the given SQL to see if is a valid query that does not
** attempt to do anything dangerous.  Return 0 on success and a
** pointer to an error message string (obtained from malloc) if







|



|







228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
  return rc;
}

/*
** Activate the query authorizer
*/
void report_restrict_sql(char **pzErr){
  db_set_authorizer(report_query_authorizer,(void*)pzErr,"Ticket-Report");
  sqlite3_limit(g.db, SQLITE_LIMIT_VDBE_OP, 10000);
}
void report_unrestrict_sql(void){
  db_clear_authorizer();
}


/*
** Check the given SQL to see if is a valid query that does not
** attempt to do anything dangerous.  Return 0 on success and a
** pointer to an error message string (obtained from malloc) if
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692

  /* Do initialization
  */
  if( pState->nCount==0 ){
    /* Turn off the authorizer.  It is no longer doing anything since the
    ** query has already been prepared.
    */
    sqlite3_set_authorizer(g.db, 0, 0);

    /* Figure out the number of columns, the column that determines background
    ** color, and whether or not this row of data is represented by multiple
    ** rows in the table.
    */
    pState->nCol = 0;
    pState->isMultirow = 0;







|







678
679
680
681
682
683
684
685
686
687
688
689
690
691
692

  /* Do initialization
  */
  if( pState->nCount==0 ){
    /* Turn off the authorizer.  It is no longer doing anything since the
    ** query has already been prepared.
    */
    db_clear_authorizer();

    /* Figure out the number of columns, the column that determines background
    ** color, and whether or not this row of data is represented by multiple
    ** rows in the table.
    */
    pState->nCol = 0;
    pState->isMultirow = 0;
Changes to src/tkt.c.
368
369
370
371
372
373
374





































































375
376
377
378
379
380
381
382
383
384
385
386
387
388

389
390

391
392

393
394
395
396
397
398
399
int ticket_change(const char *zUuid){
  const char *zConfig;
  Th_FossilInit(TH_INIT_DEFAULT);
  Th_Store("uuid", zUuid);
  zConfig = ticket_change_code();
  return Th_Eval(g.interp, 0, zConfig, -1);
}






































































/*
** Recreate the TICKET and TICKETCHNG tables.
*/
void ticket_create_table(int separateConnection){
  char *zSql;

  db_multi_exec(
    "DROP TABLE IF EXISTS ticket;"
    "DROP TABLE IF EXISTS ticketchng;"
  );
  zSql = ticket_table_schema();
  if( separateConnection ){
    if( db_transaction_nesting_depth() ) db_end_transaction(0);

    db_init_database(g.zRepositoryName, zSql, 0);
  }else{

    db_multi_exec("%s", zSql/*safe-for-%s*/);
  }

  fossil_free(zSql);
}

/*
** Repopulate the TICKET and TICKETCHNG tables from scratch using all
** available ticket artifacts.
*/







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>














>


>


>







368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
int ticket_change(const char *zUuid){
  const char *zConfig;
  Th_FossilInit(TH_INIT_DEFAULT);
  Th_Store("uuid", zUuid);
  zConfig = ticket_change_code();
  return Th_Eval(g.interp, 0, zConfig, -1);
}

/*
** An authorizer function for the SQL used to initialize the
** schema for the ticketing system.  Only allow CREATE TABLE and
** CREATE INDEX for tables whose names begin with "ticket" and
** changes to tables whose names begin with "ticket".
*/
static int ticket_schema_auth(
  void *pNErr,
  int eCode,
  const char *z0,
  const char *z1,
  const char *z2,
  const char *z3
){
  switch( eCode ){
    case SQLITE_CREATE_TABLE: {
      if( sqlite3_stricmp(z2,"main")!=0
       && sqlite3_stricmp(z2,"repository")!=0
      ){
        goto ticket_schema_error;
      }
      if( sqlite3_strnicmp(z0,"ticket",6)!=0 ){
        goto ticket_schema_error;
      }
      break;
    }
    case SQLITE_CREATE_INDEX: {
      if( sqlite3_stricmp(z2,"main")!=0
       && sqlite3_stricmp(z2,"repository")!=0
      ){
        goto ticket_schema_error;
      }
      if( sqlite3_strnicmp(z1,"ticket",6)!=0 ){
        goto ticket_schema_error;
      }
      break;
    }
    case SQLITE_INSERT:
    case SQLITE_UPDATE:
    case SQLITE_DELETE: {
      if( sqlite3_stricmp(z2,"main")!=0
       && sqlite3_stricmp(z2,"repository")!=0
      ){
        goto ticket_schema_error;
      }
      if( sqlite3_strnicmp(z0,"ticket",6)!=0
       && sqlite3_strnicmp(z0,"sqlite_",7)!=0
      ){
        goto ticket_schema_error;
      }
      break;
    }
    case SQLITE_REINDEX:
    case SQLITE_TRANSACTION:
    case SQLITE_READ: {
      break;
    }
    default: {
      goto ticket_schema_error;
    }
  }
  return SQLITE_OK;

ticket_schema_error:
  if( pNErr ) *(int*)pNErr  = 1;
  return SQLITE_DENY;
}


/*
** Recreate the TICKET and TICKETCHNG tables.
*/
void ticket_create_table(int separateConnection){
  char *zSql;

  db_multi_exec(
    "DROP TABLE IF EXISTS ticket;"
    "DROP TABLE IF EXISTS ticketchng;"
  );
  zSql = ticket_table_schema();
  if( separateConnection ){
    if( db_transaction_nesting_depth() ) db_end_transaction(0);
    db_set_authorizer(ticket_schema_auth,0,"Ticket-Schema");
    db_init_database(g.zRepositoryName, zSql, 0);
  }else{
    db_set_authorizer(ticket_schema_auth,0,"Ticket-Schema");
    db_multi_exec("%s", zSql/*safe-for-%s*/);
  }
  db_clear_authorizer();
  fossil_free(zSql);
}

/*
** Repopulate the TICKET and TICKETCHNG tables from scratch using all
** available ticket artifacts.
*/
Changes to src/url.c.
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
struct UrlData {
  int isFile;      /* True if a "file:" url */
  int isHttps;     /* True if a "https:" url */
  int isSsh;       /* True if an "ssh:" url */
  int isAlias;     /* Input URL was an alias */
  char *name;      /* Hostname for http: or filename for file: */
  char *hostname;  /* The HOST: parameter on http headers */
  const char *protocol; /* "http" or "https" or "ssh" */
  int port;        /* TCP port number for http: or https: */
  int dfltPort;    /* The default port for the given protocol */
  char *path;      /* Pathname for http: */
  char *user;      /* User id for http: */
  char *passwd;    /* Password for http: */
  char *canonical; /* Canonical representation of the URL */
  char *proxyAuth; /* Proxy-Authorizer: string */







|







48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
struct UrlData {
  int isFile;      /* True if a "file:" url */
  int isHttps;     /* True if a "https:" url */
  int isSsh;       /* True if an "ssh:" url */
  int isAlias;     /* Input URL was an alias */
  char *name;      /* Hostname for http: or filename for file: */
  char *hostname;  /* The HOST: parameter on http headers */
  const char *protocol; /* "http" or "https" or "ssh" or "file" */
  int port;        /* TCP port number for http: or https: */
  int dfltPort;    /* The default port for the given protocol */
  char *path;      /* Pathname for http: */
  char *user;      /* User id for http: */
  char *passwd;    /* Password for http: */
  char *canonical; /* Canonical representation of the URL */
  char *proxyAuth; /* Proxy-Authorizer: string */
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
** last-sync-url setting using last-sync-pw as the password.  Store
** the parser results in the pUrlData object.  Populate members of pUrlData
** as follows:
**
**      isFile      True if FILE:
**      isHttps     True if HTTPS:
**      isSsh       True if SSH:
**      protocol    "http" or "https" or "file"
**      name        Hostname for HTTP:, HTTPS:, SSH:.  Filename for FILE:
**      port        TCP port number for HTTP or HTTPS.
**      dfltPort    Default TCP port number (80 or 443).
**      path        Path name for HTTP or HTTPS.
**      user        Userid.
**      passwd      Password.
**      hostname    HOST:PORT or just HOST if port is the default.







|







74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
** last-sync-url setting using last-sync-pw as the password.  Store
** the parser results in the pUrlData object.  Populate members of pUrlData
** as follows:
**
**      isFile      True if FILE:
**      isHttps     True if HTTPS:
**      isSsh       True if SSH:
**      protocol    "http" or "https" or "file" or "ssh"
**      name        Hostname for HTTP:, HTTPS:, SSH:.  Filename for FILE:
**      port        TCP port number for HTTP or HTTPS.
**      dfltPort    Default TCP port number (80 or 443).
**      path        Path name for HTTP or HTTPS.
**      user        Userid.
**      passwd      Password.
**      hostname    HOST:PORT or just HOST if port is the default.
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
** in the global "g.url" structure as shown below.  If zUrl is NULL, then
** parse the URL given in the last-sync-url setting, taking the password
** form last-sync-pw.
**
**      g.url.isFile      True if FILE:
**      g.url.isHttps     True if HTTPS:
**      g.url.isSsh       True if SSH:
**      g.url.protocol    "http" or "https" or "file"
**      g.url.name        Hostname for HTTP:, HTTPS:, SSH:.  Filename for FILE:
**      g.url.port        TCP port number for HTTP or HTTPS.
**      g.url.dfltPort    Default TCP port number (80 or 443).
**      g.url.path        Path name for HTTP or HTTPS.
**      g.url.user        Userid.
**      g.url.passwd      Password.
**      g.url.hostname    HOST:PORT or just HOST if port is the default.







|







303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
** in the global "g.url" structure as shown below.  If zUrl is NULL, then
** parse the URL given in the last-sync-url setting, taking the password
** form last-sync-pw.
**
**      g.url.isFile      True if FILE:
**      g.url.isHttps     True if HTTPS:
**      g.url.isSsh       True if SSH:
**      g.url.protocol    "http" or "https" or "file" or "ssh"
**      g.url.name        Hostname for HTTP:, HTTPS:, SSH:.  Filename for FILE:
**      g.url.port        TCP port number for HTTP or HTTPS.
**      g.url.dfltPort    Default TCP port number (80 or 443).
**      g.url.path        Path name for HTTP or HTTPS.
**      g.url.user        Userid.
**      g.url.passwd      Password.
**      g.url.hostname    HOST:PORT or just HOST if port is the default.
Changes to src/vfile.c.
305
306
307
308
309
310
311

312
313
314
315
316
317
318
                   "  FROM vfile"
                   " WHERE id=%d AND mrid>0",
                   g.zLocalRoot, id);
  }
  while( db_step(&q)==SQLITE_ROW ){
    int id, rid, isExe, isLink;
    const char *zName;


    id = db_column_int(&q, 0);
    zName = db_column_text(&q, 1);
    rid = db_column_int(&q, 2);
    isExe = db_column_int(&q, 3);
    isLink = db_column_int(&q, 4);
    content_get(rid, &content);







>







305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
                   "  FROM vfile"
                   " WHERE id=%d AND mrid>0",
                   g.zLocalRoot, id);
  }
  while( db_step(&q)==SQLITE_ROW ){
    int id, rid, isExe, isLink;
    const char *zName;
    int n;

    id = db_column_int(&q, 0);
    zName = db_column_text(&q, 1);
    rid = db_column_int(&q, 2);
    isExe = db_column_int(&q, 3);
    isLink = db_column_int(&q, 4);
    content_get(rid, &content);
337
338
339
340
341
342
343






344
345
346
347
348
349
350
        promptFlag = 0;
      } else if( cReply!='y' && cReply!='Y' ){
        blob_reset(&content);
        continue;
      }
    }
    if( verbose ) fossil_print("%s\n", &zName[nRepos]);






    if( file_isdir(zName, RepoFILE)==1 ){
      /*TODO(dchest): remove directories? */
      fossil_fatal("%s is directory, cannot overwrite", zName);
    }
    if( file_size(zName, RepoFILE)>=0 && (isLink || file_islink(0)) ){
      file_delete(zName);
    }







>
>
>
>
>
>







338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
        promptFlag = 0;
      } else if( cReply!='y' && cReply!='Y' ){
        blob_reset(&content);
        continue;
      }
    }
    if( verbose ) fossil_print("%s\n", &zName[nRepos]);
    n = file_nondir_objects_on_path(g.zLocalRoot, zName);
    if( n ){
      fossil_fatal("cannot write %s because "
                   "non-directory object %.*s is in the way",
                   zName, n, zName);
    }
    if( file_isdir(zName, RepoFILE)==1 ){
      /*TODO(dchest): remove directories? */
      fossil_fatal("%s is directory, cannot overwrite", zName);
    }
    if( file_size(zName, RepoFILE)>=0 && (isLink || file_islink(0)) ){
      file_delete(zName);
    }