git/fsck.h
Ævar Arnfjörð Bjarmason acf9de4c94 mktag: use fsck instead of custom verify_tag()
Change the validation logic in "mktag" to use fsck's fsck_tag()
instead of its own custom parser. Curiously the logic for both dates
back to the same commit[1]. Let's unify them so we're not maintaining
two sets functions to verify that a tag is OK.

The behavior of fsck_tag() and the old "mktag" code being removed here
is different in few aspects.

I think it makes sense to remove some of those checks, namely:

 A. fsck only cares that the timezone matches [-+][0-9]{4}. The mktag
    code disallowed values larger than 1400.

    Yes there's currently no timezone with a greater offset[2], but
    since we allow any number of non-offical timezones (e.g. +1234)
    passing this through seems fine. Git also won't break in the
    future if e.g. French Polynesia decides it needs to outdo the Line
    Islands when it comes to timezone extravagance.

 B. fsck allows missing author names such as "tagger <email>", mktag
    wouldn't, but would allow e.g. "tagger [2 spaces] <email>" (but
    not "tagger [1 space] <email>"). Now we allow all of these.

 C. Like B, but "mktag" disallowed spaces in the <email> part, fsck
    allows it.

In some ways fsck_tag() is stricter than "mktag" was, namely:

 D. fsck disallows zero-padded dates, but mktag didn't care. So
    e.g. the timestamp "0000000000 +0000" produces an error now. A
    test in "t1006-cat-file.sh" relied on this, it's been changed to
    use "hash-object" (without fsck) instead.

There was one check I deemed worth keeping by porting it over to
fsck_tag():

 E. "mktag" did not allow any custom headers, and by extension (as an
    empty commit is allowed) also forbade an extra stray trailing
    newline after the headers it knew about.

    Add a new check in the "ignore" category to fsck and use it. This
    somewhat abuses the facility added in efaba7cc77 (fsck:
    optionally ignore specific fsck issues completely, 2015-06-22).

    This is somewhat of hack, but probably the least invasive change
    we can make here. The fsck command will shuffle these categories
    around, e.g. under --strict the "info" becomes a "warn" and "warn"
    becomes "error". Existing users of fsck's (and others,
    e.g. index-pack) --strict option rely on this.

    So we need to put something into a category that'll be ignored by
    all existing users of the API. Pretending that
    fsck.extraHeaderEntry=error ("ignore" by default) was set serves
    to do this for us.

1. ec4465adb3 (Add "tag" objects that can be used to sign other
   objects., 2005-04-25)

2. https://en.wikipedia.org/wiki/List_of_UTC_time_offsets

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-01-05 14:58:29 -08:00

107 lines
3.5 KiB
C

#ifndef GIT_FSCK_H
#define GIT_FSCK_H
#include "oidset.h"
#define FSCK_ERROR 1
#define FSCK_WARN 2
#define FSCK_IGNORE 3
struct fsck_options;
struct object;
void fsck_set_msg_type(struct fsck_options *options,
const char *msg_id, const char *msg_type);
void fsck_set_msg_types(struct fsck_options *options, const char *values);
int is_valid_msg_type(const char *msg_id, const char *msg_type);
/*
* callback function for fsck_walk
* type is the expected type of the object or OBJ_ANY
* the return value is:
* 0 everything OK
* <0 error signaled and abort
* >0 error signaled and do not abort
*/
typedef int (*fsck_walk_func)(struct object *obj, int type, void *data, struct fsck_options *options);
/* callback for fsck_object, type is FSCK_ERROR or FSCK_WARN */
typedef int (*fsck_error)(struct fsck_options *o,
const struct object_id *oid, enum object_type object_type,
int msg_type, const char *message);
int fsck_error_function(struct fsck_options *o,
const struct object_id *oid, enum object_type object_type,
int msg_type, const char *message);
struct fsck_options {
fsck_walk_func walk;
fsck_error error_func;
unsigned strict:1;
int *msg_type;
struct oidset skiplist;
kh_oid_map_t *object_names;
};
#define FSCK_OPTIONS_DEFAULT { NULL, fsck_error_function, 0, NULL, OIDSET_INIT }
#define FSCK_OPTIONS_STRICT { NULL, fsck_error_function, 1, NULL, OIDSET_INIT }
/* descend in all linked child objects
* the return value is:
* -1 error in processing the object
* <0 return value of the callback, which lead to an abort
* >0 return value of the first signaled error >0 (in the case of no other errors)
* 0 everything OK
*/
int fsck_walk(struct object *obj, void *data, struct fsck_options *options);
/*
* Blob objects my pass a NULL data pointer, which indicates they are too large
* to fit in memory. All other types must pass a real buffer.
*/
int fsck_object(struct object *obj, void *data, unsigned long size,
struct fsck_options *options);
/*
* fsck a tag, and pass info about it back to the caller. This is
* exposed fsck_object() internals for git-mktag(1).
*/
int fsck_tag_standalone(const struct object_id *oid, const char *buffer,
unsigned long size, struct fsck_options *options,
struct object_id *tagged_oid,
int *tag_type);
/*
* Some fsck checks are context-dependent, and may end up queued; run this
* after completing all fsck_object() calls in order to resolve any remaining
* checks.
*/
int fsck_finish(struct fsck_options *options);
/*
* Subsystem for storing human-readable names for each object.
*
* If fsck_enable_object_names() has not been called, all other functions are
* noops.
*
* Use fsck_put_object_name() to seed initial names (e.g. from refnames); the
* fsck code will extend that while walking trees, etc.
*
* Use fsck_get_object_name() to get a single name (or NULL if none). Or the
* more convenient describe_object(), which always produces an output string
* with the oid combined with the name (if any). Note that the return value
* points to a rotating array of static buffers, and may be invalidated by a
* subsequent call.
*/
void fsck_enable_object_names(struct fsck_options *options);
const char *fsck_get_object_name(struct fsck_options *options,
const struct object_id *oid);
__attribute__((format (printf,3,4)))
void fsck_put_object_name(struct fsck_options *options,
const struct object_id *oid,
const char *fmt, ...);
const char *fsck_describe_object(struct fsck_options *options,
const struct object_id *oid);
#endif