# # add_file "tests/t_add_stomp_file.at" # # add_file "tests/t_database_check_minor.at" # # patch "ChangeLog" # from [17005e6b477d1e10e723855784f06be09057eb21] # to [0b2b361c69e5acd4d580f049c7aa95f6571162dc] # # patch "commands.cc" # from [e24b9d3f4cf1db10ea68ce3733c1a8bdafb5371d] # to [e351cc76be3dbe3eaa3d9a35f69a3efe5b09c414] # # patch "database_check.cc" # from [924b949941d72064a292451e72c28bfb2e80755c] # to [63ce24deafa543d9f38e21ae826d8230e3d4d44c] # # patch "monotone.texi" # from [412d8c60e2a7e06ae07a6b8350393421d7f87b2b] # to [939f56de6aac68a630492031939ce4654f093203] # # patch "netsync.cc" # from [b68476ffc5852e56195629f4998af91221e48dcd] # to [9c25bf74dc181c19938878d5928a9fbf7a4ce25e] # # patch "sanity.cc" # from [2fd252fa148582b35ba95b8ae368e2c500d4b01f] # to [87f3bbf0785ac602b26fc8f36ff4efdf475db220] # # patch "sanity.hh" # from [e7e2010f734f9e54499e14c700017b4f0577e9ac] # to [952408e83f22dc43392c7afcffc6e556cfd80d6b] # # patch "tests/t_add_stomp_file.at" # from [] # to [ff7f16f34c202ec044ad232af78786bd119b5e98] # # patch "tests/t_database_check.at" # from [3ea731d55685488c6dadb5580b4cd04dc130e855] # to [b02fe408e53bcd4d7219183e218bc21c864dee82] # # patch "tests/t_database_check_minor.at" # from [] # to [eaba52f05e60be3f4bfc420af0722fd5ed63df02] # # patch "testsuite.at" # from [63f222bf1549f1b5ea773c38860809fd131a88e4] # to [5c3edfba27e5b828d6080f873f035114c54e7823] # # patch "transforms.cc" # from [d00be4f8e84577baaf68ab0246ae2dfedd9fdda2] # to [ba125aa4b47dbf1e00df2af72ae5d1a40b7b0175] # --- ChangeLog +++ ChangeLog @@ -1,3 +1,16 @@ +2005-04-17 Nathaniel Smith + + * sanity.{hh,cc} (E, error_failure): New sort of invariant. + * netsync.cc (process_hello_cmd): Make initial pull message + more clear and friendly. + Also, if the key has changed, that is an error, not naughtiness. + * database_check.cc (check_db): Database problems are also errors, + not naughtiness. Revamp output in case of errors, to better + distinguish non-serious errors and serious errors. + * tests/t_database_check.at: Update accordingly. + * tests/t_database_check_minor.at: New test. + * testsuite.at: Add it. + 2005-04-17 Richard Levitte * transforms.cc (glob_to_regexp): New function that takes a glob @@ -6,8 +19,23 @@ branch globs and regexps. (glob_to_regexp_test): A unit test for glob_to_regexp(). +2005-04-16 Emile Snyder + + * tests/t_add_stomp_file.at: New test for failing case. + If you have a file foo in your working dir (not monotone + controlled) and someone else adds a file foo and commits, + update should at least warn you before stomping your + non-recoverable foo file. + * testsuite.at: Add it. + 2005-04-17 Matt Johnston + * commands.cc: warn that dropkey won't truly erase the privkey + from the database + * monotone.texi: same + +2005-04-17 Matt Johnston + * database.cc: mention that it could be the filesystem that is full in the SQLITE_FULL error message --- commands.cc +++ commands.cc @@ -1184,7 +1184,10 @@ if (app.db.private_key_exists(ident)) { - P(F("dropping private key '%s' from database\n") % ident); + P(F("dropping private key '%s' from database\n\n") % ident); + W(F("the private key data may not have been erased from the")); + W(F("database. it is recommended that you use 'db dump' and")); + W(F("'db load' to be sure.")); app.db.delete_private_key(ident); key_deleted = true; } --- database_check.cc +++ database_check.cc @@ -669,22 +669,28 @@ missing_certs + mismatched_certs + unchecked_sigs + bad_sigs + missing_keys; + // unreferenced files and manifests and mismatched certs are not actually + // serious errors; odd, but nothing will break. + size_t serious = missing_files + + missing_manifests + incomplete_manifests + + missing_revisions + incomplete_revisions + + mismatched_parents + mismatched_children + + bad_history + + missing_certs + + unchecked_sigs + bad_sigs + + missing_keys; - if (total > 0) - N(total == 0, - F("check complete: %d files; %d manifests; %d revisions; %d keys; %d certs; %d problems detected\n") - % checked_files.size() - % checked_manifests.size() - % checked_revisions.size() - % checked_keys.size() - % total_certs - % total); + P(F("check complete: %d files; %d manifests; %d revisions; %d keys; %d certs\n") + % checked_files.size() + % checked_manifests.size() + % checked_revisions.size() + % checked_keys.size() + % total_certs); + P(F("total problems detected: %d (%d serious)\n") % total % serious); + if (serious) + E(false, F("serious problems detected")); + else if (total) + P(F("minor problems detected\n")); else - P(F("check complete: %d files; %d manifests; %d revisions; %d keys; %d certs; database is good\n") - % checked_files.size() - % checked_manifests.size() - % checked_revisions.size() - % checked_keys.size() - % total_certs - ); + P(F("database is good\n")); } --- monotone.texi +++ monotone.texi @@ -3813,7 +3813,10 @@ This command drops the public and/or private key. If both exist, both are dropped, if only one exists, it is dropped. This command should be used with caution as changes are irreversible without a backup of -the key(s) that were dropped. +the key(s) that were dropped. Note also that the private key is not +guaranteed to actually be erased from your database file - if you are +going to make the database file public, you should use 'db dump' +and 'db load' to import into a fresh database. @item monotone chkeypass @var{id} --- netsync.cc +++ netsync.cc @@ -1445,13 +1445,14 @@ P(F("I expected %s\n") % expected_key_hash); P(F("'monotone unset %s %s' overrides this check\n") % their_key_key.first % their_key_key.second); - N(false, F("server key changed")); + E(false, F("server key changed")); } } else { - W(F("first time connecting to server %s; authenticity can't be established\n") % peer_id); - W(F("their key is %s\n") % their_key_hash); + P(F("first time connecting to server %s\n") % peer_id); + P(F("I'll assume it's really them, but you might want to\n")); + P(F("double-check their key's fingerprint: %s\n") % their_key_hash); app.db.set_var(their_key_key, var_value(their_key_hash())); } if (!app.db.public_key_exists(their_key_hash)) --- sanity.cc +++ sanity.cc @@ -185,6 +185,17 @@ } void +sanity::error_failure(string const & expr, format const & explain, + string const & file, int line) +{ + string message; + log(format("%s:%d: detected error '%s' violated\n") % file % line % expr, + file.c_str(), line); + prefix_lines_with("error: ", explain.str(), message); + throw informative_failure(message); +} + +void sanity::invariant_failure(string const & expr, string const & file, int line) { --- sanity.hh +++ sanity.hh @@ -44,20 +44,22 @@ std::string filename; void log(boost::format const & fmt, - char const * file, int line); + char const * file, int line); void progress(boost::format const & fmt, - char const * file, int line); + char const * file, int line); void warning(boost::format const & fmt, - char const * file, int line); + char const * file, int line); void naughty_failure(std::string const & expr, boost::format const & explain, - std::string const & file, int line); + std::string const & file, int line); + void error_failure(std::string const & expr, boost::format const & explain, + std::string const & file, int line); void invariant_failure(std::string const & expr, - std::string const & file, int line); + std::string const & file, int line); void index_failure(std::string const & vec_expr, - std::string const & idx_expr, - unsigned long sz, - unsigned long idx, - std::string const & file, int line); + std::string const & idx_expr, + unsigned long sz, + unsigned long idx, + std::string const & file, int line); }; typedef std::runtime_error oops; @@ -107,7 +109,16 @@ } \ } while(0) +// E is for errors; they are normal (i.e., not a bug), but not necessarily +// attributable to user naughtiness +#define E(e, explain)\ +do { \ + if(UNLIKELY(!(e))) { \ + global_sanity.error_failure("E("#e")", (explain), __FILE__, __LINE__); \ + } \ +} while(0) + // we're interested in trapping index overflows early and precisely, // because they usually represent *very significant* logic errors. we use // an inline template function because the idx(...) needs to be used as an @@ -115,11 +126,11 @@ template inline T & checked_index(std::vector & v, - typename std::vector::size_type i, - char const * vec, - char const * index, - char const * file, - int line) + typename std::vector::size_type i, + char const * vec, + char const * index, + char const * file, + int line) { if (UNLIKELY(i >= v.size())) global_sanity.index_failure(vec, index, v.size(), i, file, line); @@ -128,11 +139,11 @@ template inline T const & checked_index(std::vector const & v, - typename std::vector::size_type i, - char const * vec, - char const * index, - char const * file, - int line) + typename std::vector::size_type i, + char const * vec, + char const * index, + char const * file, + int line) { if (UNLIKELY(i >= v.size())) global_sanity.index_failure(vec, index, v.size(), i, file, line); --- tests/t_add_stomp_file.at +++ tests/t_add_stomp_file.at @@ -0,0 +1,44 @@ +AT_SETUP([make sure update does not stomp non-monotone file]) +MONOTONE_SETUP + +# This test is a bug report +AT_XFAIL_IF(true) + +# 1. Alice checks out project, creates file foo +# 2. Bob checks out project, creates foo, adds foo, and commits +# 3. Now Alice does an update +# +# monotone should warn her before stomping her non-revision controlled 'foo' file +# + +AT_DATA(initial, [some initial data +]) + +AT_DATA(foo.alice, [foo +not revision controlled +]) + +AT_DATA(foo.bob, [foo +checked into project +]) + +# Alice make project, writes foo, but doesn't check it in +AT_CHECK(mkdir alicewd) +AT_CHECK(cp initial alicewd/initial) +AT_CHECK(MONOTONE --branch=testbranch setup alicewd, [], [ignore], [ignore]) +AT_CHECK( (cd alicewd; MONOTONE --branch=testbranch --root=. add initial), [], [ignore], [ignore]) +AT_CHECK( (cd alicewd; MONOTONE --branch=testbranch --root=. commit -m 'initial commit'), [], [ignore], [ignore]) +AT_CHECK(cp foo.alice alicewd/foo) + +# Bob does add of file foo, and commits +AT_CHECK(MONOTONE --branch=testbranch checkout bobwd, [], [ignore], [ignore]) +AT_CHECK(cp foo.bob bobwd/foo) +AT_CHECK( (cd bobwd; MONOTONE --branch=testbranch --root=. add foo), [], [ignore], [ignore]) +AT_CHECK( (cd bobwd; MONOTONE --branch=testbranch --root=. commit -m 'bob commit'), [], [ignore], [ignore]) +REV=`BASE_REVISION` + +# Alice does her update, discovers foo has been stomped! +AT_CHECK( (cd alicewd; MONOTONE --branch=testbranch --root=. update $REV), [], [ignore], [ignore]) +AT_CHECK(cmp foo.alice alicewd/foo) + +AT_CLEANUP --- tests/t_database_check.at +++ tests/t_database_check.at @@ -66,7 +66,8 @@ AT_CHECK(grep '1 missing file' stderr, [0], [ignore], [ignore]) AT_CHECK(grep '2 incomplete manifest' stderr, [0], [ignore], [ignore]) AT_CHECK(grep '2 incomplete revision' stderr, [0], [ignore], [ignore]) -AT_CHECK(grep '5 problems' stderr, [0], [ignore], [ignore]) +AT_CHECK(grep 'total problems detected: 5' stderr, [0], [ignore], [ignore]) +AT_CHECK(grep '5 serious' stderr, [0], [ignore], [ignore]) # add an unreferenced file, and an unreferenced manifest that # references several files, none of which exist in the db --- tests/t_database_check_minor.at +++ tests/t_database_check_minor.at @@ -0,0 +1,40 @@ +AT_SETUP([db check and non-serious errors]) +MONOTONE_SETUP + +# Make sure that db check detects minor problems, but doesn't complain +# about them too loudly (and doesn't exit with error status). + +AT_DATA(fileX, [blah blah +]) +AT_DATA(fileY, [stuff stuff +]) +# manifestX is: +# +# e3f2b0427b57241225ba1ffc2b67fecd64d07613 fileX +# +# where e3f2 etc. is as above + +AT_DATA(manifestX, [@<:@mdata 8e5d7e5ffca2393cfca5625ac9c1a19a46489f92@:>@ +H4sIAAAAAAAAAEs1TjNKMjAxMk8yNTcyMTQyMk1KNExLSzZKMjNPS01OMTNJMTA3MzRWUEjL +zEmN4AIA90gN9zAAAAA= +@<:@end@:>@ +]) + +ADD_FILE(testfile, [more stuff +]) +COMMIT(testbranch) +REV=`BASE_REVISION` + +AT_CHECK(MONOTONE cert $REV author extra_author, [], [ignore], [ignore]) + +AT_CHECK(MONOTONE fload < fileX, [], [ignore], [ignore]) +AT_CHECK(MONOTONE fload < fileY, [], [ignore], [ignore]) +AT_CHECK(MONOTONE read < manifestX, [], [ignore], [ignore]) + +AT_CHECK(MONOTONE db check, [], [ignore], [stderr]) + +AT_CHECK(grep -q 'problems detected: 3' stderr) +AT_CHECK(grep -q '0 serious' stderr) +AT_CHECK(grep -q 'minor problems detected' stderr) + +AT_CLEANUP --- testsuite.at +++ testsuite.at @@ -562,3 +562,5 @@ m4_include(tests/t_add_vs_commit.at) m4_include(tests/t_update_nonexistent.at) m4_include(tests/t_override_author_date.at) +m4_include(tests/t_add_stomp_file.at) +m4_include(tests/t_database_check_minor.at) --- transforms.cc +++ transforms.cc @@ -437,10 +437,23 @@ string remove_ws(string const & s) { - string tmp = s; - string::size_type pos = 0; - while ((pos = tmp.find_first_of("\n\r\t ")) != string::npos) - tmp.erase(pos,1); + string tmp; + tmp.reserve(s.size()); + for (string::const_iterator i = s.begin(); + i != s.end(); ++i) + { + switch (*i) + { + case '\n': + case '\r': + case '\t': + case ' ': + break; + default: + tmp += *i; + break; + } + } return tmp; }