Skip to content

Commit

Permalink
garmin: relax file name length rules
Browse files Browse the repository at this point in the history
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 <torvalds@linux-foundation.org>
  • Loading branch information
torvalds committed Jul 14, 2023
1 parent 67cd1cc commit 4a2dec5
Show file tree
Hide file tree
Showing 3 changed files with 24 additions and 21 deletions.
38 changes: 23 additions & 15 deletions src/garmin.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand All @@ -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
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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;
}
Expand Down
5 changes: 0 additions & 5 deletions src/garmin.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down
2 changes: 1 addition & 1 deletion src/garmin_parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down

0 comments on commit 4a2dec5

Please sign in to comment.