diff --git a/NEWS b/NEWS index fd9cad9..ccb0751 100644 --- a/NEWS +++ b/NEWS @@ -2,4 +2,6 @@ Changes between 0.1.0 and x.x.x [xx XXX xxxx] *) New function freefare_free_tag() to free individual tags from a MifareTag list. + *) The mifare_application_alloc() size parameter is now expressed in bytes + and not in sectors. diff --git a/libfreefare/mifare_application.3 b/libfreefare/mifare_application.3 index 3c72dd0..f68b027 100644 --- a/libfreefare/mifare_application.3 +++ b/libfreefare/mifare_application.3 @@ -67,7 +67,7 @@ The .Fn mifare_application_alloc function allocates enought sectors to store .Vt size -sectors for the Application Identifier +bytes for the Application Identifier .Vt aid and returns the list of allocated sectors. .Pp diff --git a/libfreefare/mifare_application.c b/libfreefare/mifare_application.c index 585172e..edb7d86 100644 --- a/libfreefare/mifare_application.c +++ b/libfreefare/mifare_application.c @@ -76,6 +76,12 @@ aidcmp (const MadAid left, const MadAid right) MifareSectorNumber * mifare_application_alloc (Mad mad, MadAid aid, size_t size) { + uint8_t sector_map[40]; + MifareSectorNumber sector; + MadAid sector_aid; + MifareSectorNumber *res = NULL; + ssize_t s = size; + /* * Ensure the card does not already have the application registered. */ @@ -85,35 +91,60 @@ mifare_application_alloc (Mad mad, MadAid aid, size_t size) return NULL; } - MifareSectorNumber *res = malloc (sizeof (*res) * (size+1)); - res[size] = 0; + for (size_t i = 0; i < sizeof (sector_map); i++) + sector_map[i] = 0; + + /* + * Try to minimize lost space and allocate as many large pages as possible + * when the target is a Mifare Classic 4k. + */ + MadAid free_aid = { 0x00, 0x00 }; + if (mad_get_version (mad) == 2) { + sector = 32; + while ((s >= 12*16) && sector < 40) { + mad_get_aid (mad, sector, §or_aid); + if (0 == aidcmp (sector_aid, free_aid)) { + sector_map[sector] = 1; + s -= 15*16; + } + sector++; + } + } + + sector = FIRST_SECTOR; + MifareSectorNumber s_max = (mad_get_version (mad) == 1) ? 15 : 31; + while ((s > 0) && (sector <= s_max)) { + mad_get_aid (mad, sector, §or_aid); + if (0 == aidcmp (sector_aid, free_aid)) { + sector_map[sector] = 1; + s -= 3*16; + } + sector++; + } /* * Ensure the remaining free space is suficient before destroying the MAD. */ - MadAid free_aid = { 0x00, 0x00 }; - MifareSectorNumber *free_aids = mifare_application_find (mad, free_aid); - if (!free_aids) + if (s > 0) return NULL; + int n = 0; + for (size_t i = FIRST_SECTOR; i < sizeof (sector_map); i++) + if (sector_map[i]) + n++; - for (size_t c = 0; c < size; c++) { - if (free_aids[c]) { - res[c] = free_aids[c]; - } else { - free (res); - res = NULL; - break; + if (!(res = malloc (n+1))) + return NULL; + + n = 0; + for (size_t i = FIRST_SECTOR; i < sizeof (sector_map); i++) + if (sector_map[i]) { + res[n] = i; + mad_set_aid (mad, i, aid); + n++; } - } - free (free_aids); - - if (res) { - /* Update the MAD */ - for (size_t c = 0; c < size; c++) - mad_set_aid (mad, res[c], aid); - } + res[n] = 0; /* Return the list of allocated sectors */ return res; diff --git a/test/test_mifare_classic_application.c b/test/test_mifare_classic_application.c index 2f72a0b..52935cd 100644 --- a/test/test_mifare_classic_application.c +++ b/test/test_mifare_classic_application.c @@ -29,14 +29,14 @@ test_mifare_classic_application (void) Mad mad = mad_new (2); cut_assert_not_null (mad, cut_message ("mad_new() failed")); - MifareSectorNumber *s_alloc = mifare_application_alloc (mad, aid, 3); + MifareSectorNumber *s_alloc = mifare_application_alloc (mad, aid, 3*3*16); cut_assert_not_null (s_alloc, cut_message ("mifare_application_alloc() failed")); MifareSectorNumber *s_found = mifare_application_find (mad, aid); cut_assert_not_null (s_found, cut_message ("mifare_application_alloc() failed")); for (int i = 0; i < 3; i++) { - cut_assert_equal_int (s_alloc[i], s_found[i], cut_message ("Allocated and foudn blocks don't match")); + cut_assert_equal_int (s_alloc[i], s_found[i], cut_message ("Allocated and found blocks don't match")); } cut_assert_equal_int (0, s_alloc[3], cut_message ("Invalid size"));