From 4a2dec531ecc74cdcd84203236b178ec0fdff782 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Fri, 14 Jul 2023 12:31:33 -0700 Subject: [PATCH] garmin: relax file name length rules We end up using the FIT file name as the "fingerprint" for the dive, and include it at the beginning of the dive data as such. And because of how Garmin encoded the FIT files, we ended up having a fixed 24-byte length for this, which is normally the date encoding: YYYY-MM-DD-HH-MM-SS.FIT with the terminating NUL character. Of course, then Garmin started using a short-form encoding too (presumably due to FAT filesystem limits), and we have magic code to sort the dates properly, using the name format YMDHMMSS.FIT with the numbers encoded in a shorter format (eg "C4ND0302.fit" is equivalent to "2022-04-23-13-03-02.fit"). See name_cmp() and parse_short_name() for details. Anyway, because we use the (zero-padded) 24 characters of the name as the fingerprint, we used a fixed-size buffer for the filename that was limited to that maximum size Garmin creates. But then you download those things, and have multiple vendors, and suddenly that 24-character limit on the filename is very annoying. Instead of fixing this in some clean and generic way, let's just raise the namelength limit to something bigger, and continue to use the first 24 characters of the name for the fingerprint. Pretty it isn't, but it makes it slightly easier to import random FIT files that don't conform exactly to the traditional Garmin format. Signed-off-by: Linus Torvalds --- src/garmin.c | 38 +++++++++++++++++++++++--------------- src/garmin.h | 5 ----- src/garmin_parser.c | 2 +- 3 files changed, 24 insertions(+), 21 deletions(-) diff --git a/src/garmin.c b/src/garmin.c index 374547c..c156097 100644 --- a/src/garmin.c +++ b/src/garmin.c @@ -138,6 +138,17 @@ garmin_device_close (dc_device_t *abstract) return DC_STATUS_SUCCESS; } +/* + * NOTE! The fingerprint is only the 24 first bytes of this, + * aka FIT_NAME_SIZE. + */ +#define FILE_NAME_SIZE 64 + +struct fit_file { + char name[FILE_NAME_SIZE + 1]; + unsigned int mtp_id; +}; + struct file_list { int nr, allocated; struct fit_file *array; @@ -175,8 +186,8 @@ static int name_cmp(const void *_a, const void *_b) const char *a_name = a->name; const char *b_name = b->name; - char a_buffer[FIT_NAME_SIZE]; - char b_buffer[FIT_NAME_SIZE]; + char a_buffer[FILE_NAME_SIZE]; + char b_buffer[FILE_NAME_SIZE]; if (strlen(a_name) == 12) { parse_short_name(a_name, a_buffer); @@ -202,19 +213,16 @@ static int check_filename(dc_device_t *abstract, const char *name) { int len = strlen(name); - const char *explain = NULL; - - DEBUG(abstract->context, " %s", name); if (len < 5) - explain = "name too short"; - if (len >= FIT_NAME_SIZE) - explain = "name too long"; + return 0; + if (len >= FILE_NAME_SIZE) + return 0; if (strncasecmp(name + len - 4, ".FIT", 4)) - explain = "name lacks FIT suffix"; + return 0; - DEBUG(abstract->context, " %s - %s", name, explain ? explain : "adding to list"); - return explain == NULL; + DEBUG(abstract->context, " %s - adding to list", name); + return 1; } static dc_status_t @@ -245,8 +253,8 @@ add_name(struct file_list *files, const char *name, unsigned int mtp_id) * will zero-pad the end of the result buffer. */ struct fit_file *entry = files->array + files->nr++; - strncpy(entry->name, name, FIT_NAME_SIZE); - entry->name[FIT_NAME_SIZE] = 0; // ensure it's null-terminated + strncpy(entry->name, name, FILE_NAME_SIZE); + entry->name[FILE_NAME_SIZE] = 0; // ensure it's null-terminated entry->mtp_id = mtp_id; } @@ -409,7 +417,7 @@ read_file(char *pathname, int pathlen, const char *name, dc_buffer_t *file) int fd, rc; pathname[pathlen] = '/'; - memcpy(pathname+pathlen+1, name, FIT_NAME_SIZE); + memcpy(pathname+pathlen+1, name, FILE_NAME_SIZE); fd = open(pathname, O_RDONLY | O_BINARY); if (fd < 0) @@ -468,7 +476,7 @@ garmin_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void // The actual dives are under the "Garmin/Activity/" directory // as FIT files, with names like "2018-08-20-10-23-30.fit". // Make sure our buffer is big enough. - if (pathlen + strlen("/Garmin/Activity/") + FIT_NAME_SIZE + 2 > PATH_MAX) { + if (pathlen + strlen("/Garmin/Activity/") + FILE_NAME_SIZE + 2 > PATH_MAX) { ERROR (abstract->context, "Invalid Garmin base directory '%s'", pathname_input); return DC_STATUS_IO; } diff --git a/src/garmin.h b/src/garmin.h index 0bf0dce..ade3d6c 100644 --- a/src/garmin.h +++ b/src/garmin.h @@ -49,11 +49,6 @@ garmin_parser_is_dive (dc_parser_t *abstract, const unsigned char *data, unsigne // special fixed header in the parser data too. #define FIT_NAME_SIZE 24 -struct fit_file { - char name[FIT_NAME_SIZE + 1]; - unsigned int mtp_id; -}; - #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/src/garmin_parser.c b/src/garmin_parser.c index ff035f5..07ae0f5 100644 --- a/src/garmin_parser.c +++ b/src/garmin_parser.c @@ -1409,7 +1409,7 @@ traverse_data(struct garmin_parser_t *garmin) if (len < FIT_NAME_SIZE) return DC_STATUS_IO; - DEBUG(garmin->base.context, "file %s", data); + DEBUG(garmin->base.context, "file %.*s", FIT_NAME_SIZE, data); data += FIT_NAME_SIZE; len -= FIT_NAME_SIZE;