/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 
/* vim: set ts=2 et sw=2 tw=80: */
/*
  Copyright 2013-2021 Stephan Beal (https://wanderinghorse.net).

  Derived heavily from previous work:

  Copyright (c) 2013 D. Richard Hipp (https://www.hwaci.com/drh/)

  
  This program is free software; you can redistribute it and/or
  modify it under the terms of the Simplified BSD License (also
  known as the "2-Clause License" or "FreeBSD License".)
  
  This program is distributed in the hope that it will be useful,
  but without any warranty; without even the implied warranty of
  merchantability or fitness for a particular purpose.
  
  *****************************************************************************
  This file contains some of the APIs dealing with the checkout state.
*/
#include "fossil-scm/fossil-internal.h"
#include "fossil-scm/fossil-hash.h"
#include "fossil-scm/fossil-checkout.h"
#include <assert.h>

/* Only for debugging */
#include <stdio.h>
#define MARKER(pfexp)                                               \
  do{ printf("MARKER: %s:%d:%s():\t",__FILE__,__LINE__,__func__);   \
    printf pfexp;                                                   \
  } while(0)




int fsl_vfile_load_from_rid(fsl_cx * f, fsl_id_t vid, char clearOthers){
  fsl_db * dbC = f ? fsl_needs_checkout(f) : NULL;
  fsl_db * dbR = dbC ? fsl_needs_repo(f) : NULL;
  fsl_deck d = fsl_deck_empty;
  fsl_stmt qIns = fsl_stmt_empty;
  fsl_stmt qRid = fsl_stmt_empty;
  int rc;
  char alreadyHad;
  fsl_card_F const * fc;
  assert(dbC && "Must only be called when a checkout is opened.");
  assert(dbR && "Must only be called when a repo is opened.");
  if(!dbC) return FSL_RC_NOT_A_CHECKOUT;
  else if(!dbR) return FSL_RC_NOT_A_REPO;
  if(vid<=0) vid = f->ckout.rid;
  assert(vid>=0);

  rc = fsl_db_transaction_begin(dbC);
  if(rc) return rc;
  alreadyHad = fsl_db_exists(dbC,
                             "SELECT 1 FROM vfile WHERE vid=%"FSL_ID_T_PFMT,
                             (fsl_id_t)vid);
  if(clearOthers){
    rc = fsl_db_exec(dbC, "DELETE FROM vfile WHERE vid!=%"FSL_ID_T_PFMT,
                     (fsl_id_t)vid);
  }
  if(alreadyHad){
    /* Already done. */
    rc = 0;
    goto end;
  }
  if(rc) goto end;

  if(0==vid){
    /* This is either misuse or an empty/initial repo with no checkins.
       Let's assume the latter, since that's what triggered this change.
    */
    goto end;
  }

  rc = fsl_deck_load_rid(f, &d, vid, FSL_SATYPE_CHECKIN);
  if(rc) goto end;
  rc = fsl_deck_F_rewind(&d);
  if(rc) goto end;
  rc = fsl_db_prepare(dbC, &qIns,
                      "INSERT INTO vfile"
                      "(vid,isexe,islink,rid,mrid,pathname,mhash) "
                      "VALUES(:vid,:isexe,:islink,:id,:id,:name,null)");
  if(rc) goto end;
  rc = fsl_db_prepare(dbR, &qRid,
                      "SELECT rid,size FROM blob WHERE uuid=?");
  if(rc) goto end;
  rc = fsl_stmt_bind_id_name(&qIns, ":vid", vid);
  while( !rc && !(rc=fsl_deck_F_next(&d, &fc)) && fc){
    fsl_id_t rid;
    int64_t size;
    if(!fc->uuid /* was removed in this version */
       || fsl_uuid_is_shunned(f,fc->uuid)) continue;
    rc = fsl_stmt_bind_text(&qRid, 1, fc->uuid, -1, 0);
    if(rc) break;
    rc = fsl_stmt_step(&qRid);
    if(FSL_RC_STEP_ROW==rc){
      rid = fsl_stmt_g_id(&qRid,0);
      size = fsl_stmt_g_int64(&qRid,1);
    }else if(FSL_RC_STEP_DONE==rc){
      rid = 0;
      size = 0;
    }else{
      assert(qRid.db->error.code);
      rc = fsl_cx_uplift_db_error(f, qRid.db);
      break;
    }
    fsl_stmt_reset(&qRid);
    if( !rid || size<0 ){
      /*
        MISSING:

        fossil_warning("content missing for %s", pFile->zName);

        We could implement that using client-provided callbacks or
        another fsl_outputer stream (stderr by default).
      */
      continue;
    }
    fsl_stmt_bind_int32_name(&qIns, ":isexe",
                             (FSL_FILE_PERM_EXE & fc->perm) ? 1 : 0);
    fsl_stmt_bind_int32_name(&qIns, ":islink",
                             (FSL_FILE_PERM_LINK & fc->perm) ? 1 : 0);
    fsl_stmt_bind_id_name(&qIns, ":id", rid);
    rc = fsl_stmt_bind_text_name(&qIns, ":name", fc->name, -1, 0);
    if(rc) break;
    rc = fsl_stmt_step(&qIns);
    if(FSL_RC_STEP_DONE!=rc) break;
    else rc = 0;
    fsl_stmt_reset(&qIns);
  }
  
  end:
  fsl_deck_finalize(&d);
  fsl_stmt_finalize(&qIns);
  fsl_stmt_finalize(&qRid);
  if(rc) fsl_db_transaction_rollback(dbC);
  else rc = fsl_db_transaction_commit(dbC);
  if(rc && !f->error.code){
    if(dbC->error.code) fsl_cx_uplift_db_error(f, dbC);
    else if(dbR->error.code) fsl_cx_uplift_db_error(f, dbR);
  }
  return rc;
}
/**
   Internal code de-duplifier for places which need to re-check a
   file's hash in order to be sure whether it was really
   modified. hashLen must be the length of the previous (db-side)
   hash of the file. This routine will hash that file using the same
   hash type.

   Returns 0 on success. On error, if *errReported is set to non-0 then the
   error state has already been set
*/
static int fsl_vfile_recheck_file_hash( fsl_cx * f, const char * zName,
                                        int hashLen, fsl_buffer * pTgt ){
  int errReported = 0;
  int rc = 0;
  pTgt->used = 0;
  if(FSL_STRLEN_SHA1==hashLen){
    rc = fsl_sha1sum_filename(zName, pTgt);
  }else if(FSL_STRLEN_K256==hashLen){
    rc = fsl_sha3sum_filename(zName, pTgt);
  }else{
    rc = fsl_cx_err_set(f, FSL_RC_CHECKSUM_MISMATCH,
                        "Cannot determine which hash to use for file: %s",
                        zName);
    errReported = 1;
  }
  if(rc && !errReported && FSL_RC_OOM != rc){
    rc = fsl_cx_err_set(f, rc, "Error %s while reading SHA of file: %s",
                        fsl_rc_cstr(rc), zName);
  }
  return rc;
}

/**
   UNTESTED single-file version of fsl_vfile_changes_scan().

   vid is the version to scan changes against. If 0 or less, the
   current checkout is used. The vfile table must have been previously
   populated with vid's contents (which it will have been for the
   current checkout).

   zFilename must refer to a file in the current checkout, possibly
   ADDed to it but not yet committed. If relativeToCwd is true
   then zFilename is resolved based on the current directory,
   otherwise it is resolved from f's checkout directory.

   If zFilename is not found in the current checkout db, or
   it is not in the filesystem but has not been deleted via
   the SCM then FSL_RC_NOT_FOUND is returned and f's error
   state contains a description of the problem.

   The checkout state of the given file is compared to its on-storage
   state, and vfile is updated to for any changes if needed.

   Returns 0 on succcess.
*/
int fsl_checkout_file_scan( fsl_cx * f, fsl_id_t vid,
                            char relativeToCwd,
                            char const * zFilename,
                            int cksigFlags){
  fsl_db * db = fsl_cx_db_checkout(f);
  fsl_stmt * q = NULL;
  fsl_stmt * stUpdate = NULL;
  fsl_buffer * nameBuf = f ? &f->fsScratch : NULL;
  fsl_buffer * buf = f ? &f->fileContent : NULL;
  char const * zCollation = fsl_cx_filename_collation(f);
  int rc;
  if(!f || !zFilename || !*zFilename) return FSL_RC_MISUSE;
  else if(f->ckout.rid<=0) return FSL_RC_NOT_A_CHECKOUT;
  assert(db);
  assert(0==nameBuf->used && "Misuse of f->fsScratch");
  if(vid<=0) vid = f->ckout.rid;
  rc = fsl_checkout_filename_check(f, relativeToCwd, zFilename, nameBuf);
  if(rc) return rc;
  zFilename = fsl_buffer_cstr(nameBuf);
  rc = fsl_db_prepare_cached(db, &q, "SELECT "
                             /*0*/"id,"
                             /*1*/"?1 || pathname,"
                             /*2*/"vfile.mrid,"
                             /*3*/"deleted,"
                             /*4*/"chnged,"
                             /*5*/"uuid,"
                             /*6*/"size,"
                             /*7*/"mtime,"
                             /*8*/"isexe,"
                             /*9*/"islink "
                             "FROM vfile LEFT JOIN "
                             "blob ON vfile.mrid=blob.rid "
                             "WHERE vid=?2 "
                             "AND pathname=?3 %s)",
                             zCollation);
  
  rc = fsl_stmt_bind_text(q, 1, f->ckout.dir, -1, 0);
  if(!rc) rc = fsl_stmt_bind_id(q, 2, vid);
  if(!rc) rc = fsl_stmt_bind_text(q, 3, zFilename, -1, 0);
  if(rc) goto end;
  rc = fsl_stmt_step(q);
  switch(rc){
    case FSL_RC_STEP_ROW:
      rc = 0;
      break;
    case FSL_RC_STEP_DONE:
      rc = fsl_cx_err_set(f, FSL_RC_NOT_FOUND,
                          "File not found in checkout: %s",
                          zFilename);
      goto end;
    default:
      assert(!rc);
      goto end;
  }


  {
    fsl_id_t id = fsl_stmt_g_id(q, 0);
    fsl_id_t rid = fsl_stmt_g_id(q, 2);
    /* fsl_size_t nName = 0; */
    char const * zName = fsl_stmt_g_text(q, 1, NULL/* &nName */);
    /* fsl_size_t const rootLen = fsl_strlen(f->ckout.dir); */
    /* char const * relName = zName + rootLen; */
    int const isDeleted = fsl_stmt_g_int32(q, 3);
    int const oldChanged = fsl_stmt_g_int32(q, 4);
    int changed = oldChanged;
    int64_t origSize = fsl_stmt_g_int64(q, 6);
    int64_t currentSize;
    fsl_time_t oldMtime = (fsl_time_t)fsl_stmt_g_int64(q, 7);
    fsl_time_t currentMtime;
    int isExe = fsl_stmt_g_int32(q, 8);
    char forceVFileUpdate = 0;
    /* fsl_card_F const * fc = NULL; */
    fsl_fstat fst = fsl_fstat_empty;
    int const useMtime = (cksigFlags & FSL_VFILE_CKSIG_SHA1)==0
      /* TODO:
         && db_get_boolean("mtime-changes", 1)
      */;
    rc = fsl_stat( zName, &fst, 1 );
    if(rc){
      if(!isDeleted){
        rc = fsl_cx_err_set(f, FSL_RC_NOT_FOUND,
                            "File missing from local checkout: %s",
                            zName);
        goto end;
      }
    }
    currentSize = rc ? -1 : (int64_t)fst.size;
    currentMtime = rc ? 0 : fst.mtime;
    rc = 0;
    if(!changed && (isDeleted || !rid)){
      /* "fossil rm" or "fossil add" always change the file */
      changed = FSL_VFILE_CHANGE_MOD;
    }
#if 0
    /* TODO: missing a piece. */
    else if( 0 <= currentSize
             /* TODO: && !fsl_checkout_is_file_or_link(zName) */){
      /* TODO:
         if( cksigFlags & CKSIG_ENOTFILE ){
           fossil_warning("not an ordinary file: %s", zName);
           nErr++;
         }
      */
      changed = FSL_VFILE_CHANGE_MOD;
    } 
#endif

#ifndef _WIN32
    if(!changed){
      /* Check for a change in the +x bit... */
      /* A change in the +x bit is definitive - the file has changed. */
      if((FSL_FSTAT_PERM_EXE & fst.perm)
         && !isExe){
        /* Used to be -x, is now +x. */
        isExe = 1;
        forceVFileUpdate = 1;
        changed = FSL_VFILE_CHANGE_MOD;
      }else if(!(FSL_FSTAT_PERM_EXE & fst.perm)
               && isExe){
        /* Used to be +x, is now -x. */
        isExe = 0;
        forceVFileUpdate = 1;
        changed = FSL_VFILE_CHANGE_MOD;
      }
    }
#endif

    if(origSize!=currentSize){
      /* A file size change is definitive - the file has changed. No
         need to check the mtime or sha1sum */
      changed = FSL_VFILE_CHANGE_MOD;
    }else if( !forceVFileUpdate
              && changed==FSL_VFILE_CHANGE_MOD
              && rid!=0 && !isDeleted ){
      /* File is believed to have changed but it is the same size.
         Double check that it really has changed by looking at content. */
      fsl_size_t nUuid = 0;
      char const * uuid;
      fsl_buffer * fileCksum = &f->fileContent;
      assert(!fileCksum->used && "Misuse of f->fileContent.");
      assert( origSize==currentSize );
      uuid = fsl_stmt_g_text(q, 5, &nUuid);
      assert(uuid && fsl_is_uuid(uuid)==(int)nUuid);
      rc = fsl_vfile_recheck_file_hash(f, zName, (int)nUuid, fileCksum);
      if(rc) goto end;
      assert(fsl_is_uuid_len((int)fileCksum->used));
      if( 0 == fsl_uuidcmp(fsl_buffer_cstr(fileCksum), uuid) ){
        changed = 0;
      }
      fileCksum->used = 0;
      /* MARKER(("SHA1 compare says %d: %s\n", changed, zName)); */
    }else if( !forceVFileUpdate
              && (changed==FSL_VFILE_CHANGE_NONE
                  || changed==FSL_VFILE_CHANGE_MERGE_MOD
                  || changed==FSL_VFILE_CHANGE_INTEGRATE_MOD)
              && (useMtime==0 || currentMtime!=oldMtime) ){
      /* For files that were formerly believed to be unchanged or that were
         changed by merging, if their mtime changes, or unconditionally
         if --sha1sum is used, check to see if they have been edited by
         looking at their hash */
      fsl_size_t nUuid = 0;
      char const * uuid;
      fsl_buffer * fileCksum = &f->fileContent;
      assert(!fileCksum->used && "Misuse of f->fileContent.");
      assert( origSize==currentSize );
      uuid = fsl_stmt_g_text(q, 5, &nUuid);
      assert(uuid && fsl_is_uuid(uuid)==(int)nUuid);
      rc = fsl_vfile_recheck_file_hash(f, zName, (int)nUuid, fileCksum);
      if(rc) goto end;
      assert(fsl_is_uuid_len((int)fileCksum->used));
      if( fsl_uuidcmp(fsl_buffer_cstr(fileCksum), uuid) ){
        changed = FSL_VFILE_CHANGE_MOD;
      }
      fileCksum->used = 0;
      /* MARKER(("SHA1 compare says %d: %s\n", changed, zName)); */
    }
    if( (cksigFlags & FSL_VFILE_CKSIG_SETMTIME)
        && (changed==FSL_VFILE_CHANGE_NONE
               || changed==FSL_VFILE_CHANGE_MERGE_MOD
               || changed==FSL_VFILE_CHANGE_INTEGRATE_MOD) ){
      fsl_time_t desiredMtime = 0;
      if( 0==fsl_mtime_of_manifest_file(f, vid, rid, &desiredMtime)){
        if( currentMtime != desiredMtime ){
          fsl_file_mtime_set(zName, desiredMtime);
          currentMtime = fsl_file_mtime(zName);
          /* MARKER(("LARGELY UNTESTED FSL_VFILE_CKSIG_SETMTIME set mtime of: %s\n", zName)); */
        }
      }
    }

    if( forceVFileUpdate || currentMtime!=oldMtime || changed!=oldChanged ){
      if(!stUpdate){
        rc = fsl_db_prepare_cached(db, &stUpdate,
                                   "UPDATE vfile "
                                   "SET mtime=?, chnged=?, isexe=? "
                                   "WHERE id=?");
        if(rc) goto end;
      }else{
        fsl_stmt_reset(stUpdate);
      }
      fsl_stmt_bind_int64(stUpdate, 1, currentMtime);
      fsl_stmt_bind_int32(stUpdate, 2, changed);
      fsl_stmt_bind_int32(stUpdate, 3, isExe);
      fsl_stmt_bind_id(stUpdate, 4, id);
      rc = fsl_stmt_step(stUpdate);
      if(FSL_RC_STEP_DONE==rc) rc = 0;
      /* MARKER(("UPDATED vfile.(mtime,chnged) for: %s\n", zName)); */
    }
  }

  end:
  fsl_stmt_cached_yield(q);
  fsl_stmt_cached_yield(stUpdate);
  fsl_cx_yield_file_buffer(f);
  assert(0==buf->used);
  return rc;
}

int fsl_vfile_changes_scan(fsl_cx * f, fsl_id_t vid, int cksigFlags){
  fsl_stmt * stUpdate = NULL;
  fsl_stmt q = fsl_stmt_empty;
  int rc = 0;
  fsl_buffer origCksum = fsl_buffer_empty;
  fsl_db * db = fsl_needs_checkout(f);
  fsl_fstat fst = fsl_fstat_empty;
  fsl_deck deck = fsl_deck_empty;
#ifndef _WIN32
  fsl_deck * d = NULL;
#endif
  fsl_size_t rootLen;

  if(!db) return FSL_RC_NOT_A_CHECKOUT;

  assert(f->ckout.dir);
  if(vid<0) vid = f->ckout.rid;
  assert(vid>=0);
  rootLen = fsl_strlen(f->ckout.dir);
  assert(rootLen);

  rc = fsl_db_transaction_begin(db);
  if(rc) return rc;

  rc = fsl_vfile_load_from_rid(f, vid,
                               FSL_VFILE_CKSIG_CLEAR_VFILE & cksigFlags);
  if(rc) goto end;

  if(vid>0){
    rc = fsl_deck_load_rid(f, &deck, vid, FSL_SATYPE_CHECKIN);
    if(rc) goto end;
#ifndef _WIN32
    d = &deck;
#endif
  }

  rc = fsl_db_prepare(db, &q, "SELECT "
                      /*0*/"id,"
                      /*1*/"%Q || pathname,"
                      /*2*/"vfile.mrid,"
                      /*3*/"deleted,"
                      /*4*/"chnged,"
                      /*5*/"uuid,"
                      /*6*/"size,"
                      /*7*/"mtime,"
                      /*8*/"isexe,"
                      /*9*/"islink "
                      "FROM vfile LEFT JOIN blob ON vfile.mrid=blob.rid "
                      "WHERE vid=%"FSL_ID_T_PFMT,
                      f->ckout.dir, (fsl_id_t)vid);
  if(rc) goto end;
  while( fsl_stmt_step(&q) == FSL_RC_STEP_ROW ){
    fsl_id_t id, rid;
    char const * zName;
#ifndef _WIN32
    char const * relName;
#endif
    fsl_size_t nName = 0;
    int isDeleted;
    int64_t currentSize;
    int64_t origSize;
    int changed, oldChanged;
    int isExe;
    char forceVFileUpdate = 0;
    fsl_time_t oldMtime, currentMtime;
#ifndef _WIN32
    fsl_card_F const * fc = NULL;
#endif
    int const useMtime = (cksigFlags & FSL_VFILE_CKSIG_SHA1)==0
      /* TODO:
         && db_get_boolean("mtime-changes", 1)
      */;
    id = fsl_stmt_g_id(&q, 0);
    assert(id>0);
    zName = fsl_stmt_g_text(&q, 1, &nName);
#ifndef _WIN32
    relName = zName + rootLen;
#endif
    rid = fsl_stmt_g_id(&q, 2);
    isDeleted = fsl_stmt_g_int32(&q, 3);
    oldChanged = changed = fsl_stmt_g_int32(&q, 4);
    origSize = fsl_stmt_g_int64(&q, 6);
    oldMtime = (fsl_time_t)fsl_stmt_g_int64(&q, 7);
    isExe = fsl_stmt_g_int32(&q, 8);
    rc = fsl_stat( zName, &fst, 1 );
    currentSize = rc ? -1 : (int64_t)fst.size;
    currentMtime = rc ? 0 : fst.mtime;
    rc = 0;
    if(!changed && (isDeleted || !rid)){
      /* ADD and REMOVE operations always change the file */
      changed = FSL_VFILE_CHANGE_MOD;
    }
#if 0
    /* TODO: missing a piece. */
    else if( 0 <= currentSize
             /* TODO: && !fsl_checkout_is_file_or_link(zName) */){
      /* TODO:
         if( cksigFlags & CKSIG_ENOTFILE ){
           fossil_warning("not an ordinary file: %s", zName);
           nErr++;
         }
      */
      changed = FSL_VFILE_CHANGE_MOD;
    } 
#endif
#ifndef _WIN32
    if(!changed && d && (fc = fsl_deck_F_search(d, relName))){
      /*
        We will almost always have an F-card match, but won't on added
        files and possibly(?) added-via-merge files.
      */
      /* A change in the +x bit is definitive - the file has changed. */
      if((FSL_FSTAT_PERM_EXE & fst.perm)
         && (FSL_FILE_PERM_EXE != fc->perm)){
        /* Used to be -x, is now +x. */
        isExe = 1;
        forceVFileUpdate = 1;
        changed = FSL_VFILE_CHANGE_MOD;
      }else if(!(FSL_FSTAT_PERM_EXE & fst.perm)
               && (FSL_FILE_PERM_EXE == fc->perm)){
        /* Used to be +x, is now -x. */
        isExe = 0;
        forceVFileUpdate = 1;
        changed = FSL_VFILE_CHANGE_MOD;
      }
    }
#endif
    if(origSize!=currentSize){
      if( changed!=FSL_VFILE_CHANGE_MOD ){
        /* A file size change is definitive - the file has changed. No
           need to check the mtime or sha1sum */
        changed = FSL_VFILE_CHANGE_MOD;
      }
    }else if( !forceVFileUpdate
              && changed==FSL_VFILE_CHANGE_MOD
              && rid!=0 && !isDeleted ){
      /* File is believed to have changed but it is the same size.
         Double check that it really has changed by looking at content. */
      fsl_size_t nUuid = 0;
      char const * uuid;
      fsl_buffer * fileCksum = &f->fileContent;
      assert(!fileCksum->used && "Misuse of f->fileContent.");
      assert( origSize==currentSize );
      uuid = fsl_stmt_g_text(&q, 5, &nUuid);
      assert(uuid && fsl_is_uuid_len((int)nUuid));
      rc = fsl_vfile_recheck_file_hash(f, zName, (int)nUuid, fileCksum);
      if(rc) goto end;
      assert(fsl_is_uuid_len((int)fileCksum->used));
      if( 0 == fsl_uuidcmp(fsl_buffer_cstr(fileCksum), uuid) ){
        changed = 0;
      }
      fileCksum->used = 0;
      /* MARKER(("SHA1 compare says %d: %s\n", changed, zName)); */
    }else if( !forceVFileUpdate
              && (changed==FSL_VFILE_CHANGE_NONE
                  || changed==FSL_VFILE_CHANGE_MERGE_MOD
                  || changed==FSL_VFILE_CHANGE_INTEGRATE_MOD)
              && (useMtime==0 || currentMtime!=oldMtime) ){
      /* For files that were formerly believed to be unchanged or that were
         changed by merging, if their mtime changes, or unconditionally
         if --sha1sum is used, check to see if they have been edited by
         looking at their SHA1 sum */
      fsl_size_t nUuid = 0;
      char const * uuid;
      fsl_buffer * fileCksum = &f->fileContent;
      assert(!fileCksum->used && "Misuse of f->fileContent.");
      assert( origSize==currentSize );
      uuid = fsl_stmt_g_text(&q, 5, &nUuid);
      assert(uuid && fsl_is_uuid_len((int)nUuid));
      rc = fsl_vfile_recheck_file_hash(f, zName, (int)nUuid, fileCksum);
      if(rc) goto end;
      assert(fsl_is_uuid_len((int)fileCksum->used));
      if( fsl_uuidcmp(fsl_buffer_cstr(fileCksum), uuid) ){
        changed = FSL_VFILE_CHANGE_MOD;
      }
      fileCksum->used = 0;
      /* MARKER(("SHA1 compare says %d: %s\n", changed, zName)); */
    }
    if( (cksigFlags & FSL_VFILE_CKSIG_SETMTIME)
        && (changed==FSL_VFILE_CHANGE_NONE
               || changed==FSL_VFILE_CHANGE_MERGE_MOD
               || changed==FSL_VFILE_CHANGE_INTEGRATE_MOD) ){
      fsl_time_t desiredMtime = 0;
      if( 0==fsl_mtime_of_manifest_file(f, vid, rid, &desiredMtime)){
        if( currentMtime != desiredMtime ){
          fsl_file_mtime_set(zName, desiredMtime);
          currentMtime = fsl_file_mtime(zName);
          /* MARKER(("LARGELY UNTESTED FSL_VFILE_CKSIG_SETMTIME set mtime of: %s\n", zName)); */
        }
      }
    }

    if( forceVFileUpdate || currentMtime!=oldMtime || changed!=oldChanged ){
      if(!stUpdate){
        rc = fsl_db_prepare_cached(db, &stUpdate,
                                   "UPDATE vfile SET "
                                   "mtime=?1,"
                                   "chnged=?2, "
                                   "isexe=?3 "
                                   "WHERE id=?4");
        if(rc) goto end;
      }else{
        fsl_stmt_reset(stUpdate);
      }
      fsl_stmt_bind_int64(stUpdate, 1, currentMtime);
      fsl_stmt_bind_int32(stUpdate, 2, changed);
      fsl_stmt_bind_int32(stUpdate, 3, isExe);
      fsl_stmt_bind_id(stUpdate, 4, id);
      rc = fsl_stmt_step(stUpdate);
      if(FSL_RC_STEP_DONE!=rc) goto end;
      rc = 0;
      /* MARKER(("UPDATED vfile.(mtime,chnged) for: %s\n", zName)); */
    }
  }/*while(step)*/
  end:
  if(rc && db->error.code && !f->error.code){
    fsl_cx_uplift_db_error(f, db);
  }
  if(rc) {
    fsl_db_transaction_rollback(db);
  }else{
    rc = fsl_db_transaction_commit(db);
    if(rc && db->error.code && !f->error.code){
      fsl_cx_uplift_db_error(f, db);
    }
  }
  fsl_deck_finalize(&deck);
  fsl_stmt_cached_yield(stUpdate);
  fsl_stmt_finalize(&q);
  fsl_buffer_clear(&origCksum);
  return rc;
}


#undef MARKER
