Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Improved the checkin-to-closed-leaf check to allow checkin if the branch would change. Ported in the reserved filename checks, so that 'add' ops will fail if the user adds a filename which is illegal. |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA1: |
2d90220116bb90644f9ac14dd39f0785 |
User & Date: | stephan 2021-03-01 12:31:47 |
Context
2021-03-01
| ||
13:45 | Renamed FCliCommand to fcli_command for consistency's sake. check-in: 92aa86396d user: stephan tags: trunk | |
12:31 | Improved the checkin-to-closed-leaf check to allow checkin if the branch would change. Ported in the reserved filename checks, so that 'add' ops will fail if the user adds a filename which is illegal. check-in: 2d90220116 user: stephan tags: trunk | |
07:57 | Corrected the sameLine() merge-internal algo, so merge collisions are now detected. Minor internal merge/merge-adjacent cleanups. check-in: ecfc36587c user: stephan tags: trunk | |
Changes
Changes to include/fossil-scm/fossil-checkout.h.
︙ | ︙ | |||
499 500 501 502 503 504 505 | The user name for the checkin. If NULL or empty, it defaults to fsl_cx_user_get(). If that is NULL, a FSL_RC_RANGE error is triggered. */ char const * user; /** | < < | 499 500 501 502 503 504 505 506 507 508 509 510 511 512 | The user name for the checkin. If NULL or empty, it defaults to fsl_cx_user_get(). If that is NULL, a FSL_RC_RANGE error is triggered. */ char const * user; /** If not NULL, makes the checkin the start of a new branch with this name. */ char const * branch; /** If this->branch is not NULL, this is applied as its "bgcolor" |
︙ | ︙ |
Changes to include/fossil-scm/fossil-internal.h.
︙ | ︙ | |||
1474 1475 1476 1477 1478 1479 1480 | eventually be fsl_free()'d by the caller. On error *outRaw is not modified. */ FSL_EXPORT int fsl_diff_text_raw(fsl_buffer const *p1, fsl_buffer const *p2, int diffFlags, int ** outRaw); /** | | > | | > | | < | < | | > | < < | > | | | < | < | | | 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 | eventually be fsl_free()'d by the caller. On error *outRaw is not modified. */ FSL_EXPORT int fsl_diff_text_raw(fsl_buffer const *p1, fsl_buffer const *p2, int diffFlags, int ** outRaw); /** If the given file name is a reserved filename (case-insensitive) on Windows platforms, a pointer to the reserved part of the name, else NULL is returned. zPath must be a canonical path with forward-slash directory separators. */ FSL_EXPORT bool fsl_is_reserved_fn_windows(const char *zPath); /** Uses fsl_is_reserved_fn() to determine whether zPath is legal. If it is, 0 is returned, else FSL_RC_MISUSE (or FSL_RC_OOM) is returned and f's error state is updated to indicate the nature of the problem. nFile is the length of zPath. If negative, fsl_strlen() is used to determine its length. TODO/FIXME: confirm that zPath does not refer to the current repository db. In practice those normally live outside of the checkout dir, so cannot be added to a repo, but late-2020 additions to fossil(1)'s 'open' support makes it easy to have the repo db in the checkout dir. (Some people use them that way even without that feature.) */ FSL_EXPORT int fsl_reserved_fn_check(fsl_cx *f, const char *zPath, fsl_int_t nFile); #if defined(__cplusplus) } /*extern "C"*/ #endif #endif /* NET_FOSSIL_SCM_FSL_INTERNAL_H_INCLUDED */ |
Changes to include/fossil-scm/fossil-repo.h.
︙ | ︙ | |||
2796 2797 2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 2808 2809 | If FSL_RC_NOT_FOUND is returned and hashOut is not NULL, *hashOut is set to the value of f's preferred hash. *ridOut is only modified if 0 is returned, in which case *ridOut will have a positive value. */ FSL_EXPORT int fsl_repo_blob_lookup( fsl_cx * f, fsl_buffer const * src, fsl_id_t * ridOut, fsl_uuid_str * hashOut ); #if 0 /** NOT YET IMPLEMENTED - just thinking out loud here. */ struct fsl_repo_open_opt { | > > > > > > > > > > > > > > > > | 2796 2797 2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 | If FSL_RC_NOT_FOUND is returned and hashOut is not NULL, *hashOut is set to the value of f's preferred hash. *ridOut is only modified if 0 is returned, in which case *ridOut will have a positive value. */ FSL_EXPORT int fsl_repo_blob_lookup( fsl_cx * f, fsl_buffer const * src, fsl_id_t * ridOut, fsl_uuid_str * hashOut ); /** Returns true if the specified file name ends with any reserved name, e.g.: _FOSSIL_ or .fslckout. For the sake of efficiency, zFilename must be a canonical name, e.g. an absolute path using only forward slash ('/') as a directory separator. On Windows builds, this also checks for reserved Windows filenames, e.g. "CON" and "PRN". nameLen must be the length of zFilename. If it is negative, fsl_strlen() is used to calculate it. */ FSL_EXPORT bool fsl_is_reserved_fn(const char *zFilename, fsl_int_t nameLen ); #if 0 /** NOT YET IMPLEMENTED - just thinking out loud here. */ struct fsl_repo_open_opt { |
︙ | ︙ |
Changes to include/fossil-scm/fossil-util.h.
︙ | ︙ | |||
4195 4196 4197 4198 4199 4200 4201 4202 4203 4204 4205 4206 | /** Frees any memory owned by p, but does not free p. */ FSL_EXPORT void fsl_id_bag_clear(fsl_id_bag *p); #if defined(__cplusplus) } /*extern "C"*/ #endif #endif /* NET_FOSSIL_SCM_FSL_UTIL_H_INCLUDED */ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 4195 4196 4197 4198 4199 4200 4201 4202 4203 4204 4205 4206 4207 4208 4209 4210 4211 4212 4213 4214 4215 4216 4217 4218 4219 4220 4221 4222 4223 4224 4225 4226 4227 4228 4229 4230 4231 4232 4233 4234 4235 4236 | /** Frees any memory owned by p, but does not free p. */ FSL_EXPORT void fsl_id_bag_clear(fsl_id_bag *p); /** Returns true if p contains a fossil-format merge conflict marker, else returns false. @see fsl_buffer_merge3() */ FSL_EXPORT bool fsl_buffer_contains_merge_marker(fsl_buffer const *p); /** Performs a three-way merge. The merge is an edit against pV2. Both pV1 and pV2 have a common origin at pPivot. Apply the changes of pPivot ==> pV1 to pV2, appending them to pOut. (Pedantic side-note: the input buffers are not const because we need to manipulate their cursors.) If merge conflicts are encountered, it continues as best as it can and injects "indiscrete" markers in the output to denote the nature of each conflict. If conflictCount is not NULL then on success the number of merge conflicts is written to *conflictCount. Returns 0 on success, FSL_RC_OOM on OOM, FSL_RC_TYPE if any input appears to be binary. @see fsl_buffer_contains_merge_marker() */ FSL_EXPORT int fsl_buffer_merge3(fsl_buffer *pPivot, fsl_buffer *pV1, fsl_buffer *pV2, fsl_buffer *pOut, unsigned int *conflictCount); #if defined(__cplusplus) } /*extern "C"*/ #endif #endif /* NET_FOSSIL_SCM_FSL_UTIL_H_INCLUDED */ |
Changes to src/checkin.c.
︙ | ︙ | |||
167 168 169 170 171 172 173 | fsl_id_t vfid = 0; char const * path = fsl_buffer_cstr(&canon); char dirCheck; fsl_buffer * buf = &f->fsScratch; fsl_size_t const oldBagSize = f->ckin.selectedIds.entryCount; assert(!buf->used && "Misuse of f->fsScratch"); | > > > > > > > > > > > > > > > | | 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 | fsl_id_t vfid = 0; char const * path = fsl_buffer_cstr(&canon); char dirCheck; fsl_buffer * buf = &f->fsScratch; fsl_size_t const oldBagSize = f->ckin.selectedIds.entryCount; assert(!buf->used && "Misuse of f->fsScratch"); #if 0 rc = fsl_reserved_fn_check(f, path, (fsl_int_t)canon.used); /* Potential problem here: reserved name checking was not added to fossil until late 2020, at which point we know for a fact that there were older repositories which had managed to add _FOSSIL_ db files, and similar, to subdirectories of their repos. If we reject such files here, we will prohibit users from checking in changes to those files they've already got in their repos. Thus we should arguably restrict this check to the fsl_checkout_file_add() operation, which would only prohibit the addition of new reserved filenames without affecting ones which might have slipped in already. */ #endif if(!rc) rc = fsl_filename_to_vfile_id(f, path, &vfid); if(rc) goto out; else if(vfid>0 && fsl_id_bag_contains(&f->ckin.selectedIds, vfid)){ /* A single file matching a vfile entry... */ rc = 0; }else{ /* Try interpreting it as a directory... */ while('/' == canon.mem[canon.used-1]){ |
︙ | ︙ | |||
942 943 944 945 946 947 948 | ? opt->closeBranch : NULL); RC; } { /* | < < | 957 958 959 960 961 962 963 964 965 966 967 968 969 970 | ? opt->closeBranch : NULL); RC; } { /* Close any INTEGRATE merges if !op->integrate, or type-0 and integrate merges if opt->integrate. */ rc = fsl_db_prepare(dbC, &q, "SELECT mhash, merge FROM vmerge " " WHERE id %s ORDER BY 1", opt->integrate ? "IN(0,-4)" : "=(-4)"); |
︙ | ︙ | |||
1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 | (fsl_id_t)rid); if(rc){ fsl_cx_uplift_db_error(f, r); } return rc; } int fsl_checkin_commit(fsl_cx * f, fsl_checkin_opt const * opt, fsl_id_t * newRid, fsl_uuid_str * newUuid ){ int rc; fsl_deck deck = fsl_deck_empty; fsl_deck *d = &deck; fsl_db * dbC; fsl_db * dbR; char inTrans = 0; char oldPrivate; int const oldFlags = f ? f->flags : 0; fsl_id_t const vid = f ? f->ckout.rid : 0; if(!f || !opt) return FSL_RC_MISUSE; else if(!(dbC = fsl_needs_checkout(f))) return FSL_RC_NOT_A_CHECKOUT; else if(!(dbR = fsl_needs_repo(f))) return FSL_RC_NOT_A_REPO; assert(vid>=0); | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > | < < > | | 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 | (fsl_id_t)rid); if(rc){ fsl_cx_uplift_db_error(f, r); } return rc; } /** Returns true if the given blob RID is has a "closed" tag. This is generally intended only to be passed the RID of the current checkout, before attempting to perform a commit against it. */ static bool fsl_leaf_is_closed(fsl_cx * f, fsl_id_t rid){ fsl_db * const dbR = fsl_needs_repo(f); return dbR ? fsl_db_exists(dbR, "SELECT 1 FROM tagxref" " WHERE tagid=%d " " AND rid=%"FSL_ID_T_PFMT" AND tagtype>0", FSL_TAGID_CLOSED, rid) : false; } /** Returns true if the given name is the current branch for the given checkin version. */ static bool fsl_is_current_branch(fsl_db * dbR, fsl_id_t vid, char const * name){ return fsl_db_exists(dbR, "SELECT 1 FROM tagxref" " WHERE tagid=%d AND rid=%"FSL_ID_T_PFMT " AND tagtype>0" " AND value=%Q", FSL_TAGID_BRANCH, vid, name); } int fsl_checkin_commit(fsl_cx * f, fsl_checkin_opt const * opt, fsl_id_t * newRid, fsl_uuid_str * newUuid ){ int rc; fsl_deck deck = fsl_deck_empty; fsl_deck *d = &deck; fsl_db * dbC; fsl_db * dbR; char inTrans = 0; char oldPrivate; int const oldFlags = f ? f->flags : 0; fsl_id_t const vid = f ? f->ckout.rid : 0; if(!f || !opt) return FSL_RC_MISUSE; else if(!(dbC = fsl_needs_checkout(f))) return FSL_RC_NOT_A_CHECKOUT; else if(!(dbR = fsl_needs_repo(f))) return FSL_RC_NOT_A_REPO; assert(vid>=0); /** Do not permit a checkin to a closed leaf unless opt->branch would switch us to a new branch. */ if( fsl_leaf_is_closed(f, vid) && (!opt->branch || !*opt->branch || fsl_is_current_branch(dbR, vid, opt->branch))){ return fsl_cx_err_set(f, FSL_RC_MISUSE, "Cannot commit against a closed leaf."); } fsl_cx_err_reset(f) /* avoid propagating an older error by accident. Did that in test code. */; |
︙ | ︙ |
Changes to src/checkout.c.
︙ | ︙ | |||
283 284 285 286 287 288 289 290 291 292 293 294 295 296 | if(!f) return FSL_RC_MISUSE; else if(!db) return FSL_RC_NOT_A_CHECKOUT; assert(vid>=0); rc = fsl_cx_stat2(f, relativeToCwd, zFilename, &fst, &fname, 0); if(rc) goto end; zNorm = fsl_buffer_cstr(&fname); if( fsl_db_exists(db, "SELECT 1 FROM vfile" " WHERE vid=%"FSL_ID_T_PFMT " AND pathname=%Q %s", (fsl_id_t)vid, zNorm, fsl_cx_filename_collation(f)) ){ rc = fsl_db_exec(db, "UPDATE vfile SET deleted=0," " mtime=%"PRIi64 | > > > | 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 | if(!f) return FSL_RC_MISUSE; else if(!db) return FSL_RC_NOT_A_CHECKOUT; assert(vid>=0); rc = fsl_cx_stat2(f, relativeToCwd, zFilename, &fst, &fname, 0); if(rc) goto end; zNorm = fsl_buffer_cstr(&fname); rc = fsl_reserved_fn_check(f, zNorm, (fsl_int_t)fname.used); if(rc) goto end; if( fsl_db_exists(db, "SELECT 1 FROM vfile" " WHERE vid=%"FSL_ID_T_PFMT " AND pathname=%Q %s", (fsl_id_t)vid, zNorm, fsl_cx_filename_collation(f)) ){ rc = fsl_db_exec(db, "UPDATE vfile SET deleted=0," " mtime=%"PRIi64 |
︙ | ︙ | |||
511 512 513 514 515 516 517 518 | *rv = fnid; }else if(db->error.code){ fsl_cx_uplift_db_error(f, db); } return rc; } #undef MARKER | > > > > > > > > > > > > | 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 | *rv = fnid; }else if(db->error.code){ fsl_cx_uplift_db_error(f, db); } return rc; } FSL_EXPORT int fsl_reserved_fn_check(fsl_cx *f, const char *zPath, fsl_int_t nPath){ int rc = 0; if(nPath<0) nPath = (fsl_int_t)fsl_strlen(zPath); if(fsl_is_reserved_fn(zPath, nPath)){ rc = fsl_cx_err_set(f, FSL_RC_MISUSE, "Filename is reserved, not legal " "for adding to a repository: %.*s", (int)nPath, zPath); } return rc; } #undef MARKER |
Changes to src/fsl.c.
︙ | ︙ | |||
983 984 985 986 987 988 989 | } char fsl_isatty(int fd){ return isatty(fd) ? 1 : 0; } | > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 | } char fsl_isatty(int fd){ return isatty(fd) ? 1 : 0; } bool fsl_is_reserved_fn_windows(const char *zPath){ static const char *const azRes[] = { "CON", "PRN", "AUX", "NUL", "COM", "LPT" }; unsigned int i; while( zPath[0] ){ for(i=0; i<sizeof(azRes)/sizeof(azRes[0]); ++i){ if( fsl_strnicmp(zPath, azRes[i], 3)==0 && ((i>=4 && fsl_isdigit(zPath[3]) && (zPath[4]=='/' || zPath[4]=='.' || zPath[4]==0)) || (i<4 && (zPath[3]=='/' || zPath[3]=='.' || zPath[3]==0))) ){ return true; } } while( zPath[0] && zPath[0]!='/' ) ++zPath; while( zPath[0]=='/' ) ++zPath; } return false; } bool fsl_is_reserved_fn(const char *zFilename, fsl_int_t nameLen){ fsl_size_t nFilename = nameLen>=0 ? (fsl_size_t)nameLen : fsl_strlen(zFilename); char const * zEnd; int gotSuffix = 0; assert( zFilename && "API misuse" ); #if FSL_PLATFORM_IS_WINDOWS if(nFilename>2 && fsl_is_reserved_fn_windows(zFilename)){ return true; } #endif if( nFilename<8 ) return false; /* 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( fsl_strnicmp("wal", &zEnd[-3], 3) && fsl_strnicmp("shm", &zEnd[-3], 3) ){ return false; } gotSuffix = 4; }else if( nFilename>=16 && zEnd[-8]=='-' ){ /*strlen(_FOSSIL_-journal) */ if( fsl_strnicmp("journal", &zEnd[-7], 7) ) return false; 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( fsl_strnicmp("_FOSSIL_", &zEnd[-8], 8) ) return false; if( 8==nFilename ) return true; return zEnd[-9]=='/' ? true : !!gotSuffix; } case 'T': case 't':{ if( nFilename<9 || zEnd[-9]!='.' || fsl_strnicmp(".fslckout", &zEnd[-9], 9) ){ return false; } if( 9==nFilename ) return 1; return zEnd[-10]=='/' ? true : !!gotSuffix; } default:{ return false; } } } #undef MARKER #if defined(_WIN32) || defined(WIN32) #undef isatty #endif |