diff options
Diffstat (limited to 'lib-src')
| -rw-r--r-- | lib-src/ChangeLog | 33 | ||||
| -rw-r--r-- | lib-src/update-game-score.c | 221 |
2 files changed, 169 insertions, 85 deletions
diff --git a/lib-src/ChangeLog b/lib-src/ChangeLog index dc4ec91c512..50273e2a60a 100644 --- a/lib-src/ChangeLog +++ b/lib-src/ChangeLog | |||
| @@ -1,3 +1,36 @@ | |||
| 1 | 2014-01-19 Paul Eggert <eggert@cs.ucla.edu> | ||
| 2 | |||
| 3 | update-game-score fixes for -m and integer overflow (Bug#16428) | ||
| 4 | * update-game-score.c: Include inttypes.h, stdbool.h. | ||
| 5 | (min): New macro, if not already defined. | ||
| 6 | (MAX_SCORES, main): Limit the maximum number of scores only from | ||
| 7 | limits imposed by the underyling platform, instead of the | ||
| 8 | arbitrary value 200. | ||
| 9 | (struct score_entry, main, read_score, write_score): | ||
| 10 | Scores are now intmax_t, not long. | ||
| 11 | (get_user_id): Reject user names containing spaces or newlines, | ||
| 12 | as they would mess up the score file. | ||
| 13 | Allow uids that don't fit in 'long'. | ||
| 14 | Increase the size of the buffer, to avoid overrun in weird cases. | ||
| 15 | (get_prefix, main): Use bool for boolean. | ||
| 16 | (main): Rewrite expr to avoid possibility of signed integer | ||
| 17 | overflow. Don't allow newlines in data, as this would mess up | ||
| 18 | the score file. Check for memory allocation failure when adding | ||
| 19 | the new score, or when unlockint the file. Implement -m. | ||
| 20 | (read_score): Check for integer overflow when reading a score. | ||
| 21 | (read_score) [!HAVE_GETDELIM]: Check for integer overflow when | ||
| 22 | data gets very long. Check only for space to delimit names, | ||
| 23 | since that's what's done in the HAVE_GETDELIM case. | ||
| 24 | (read_scores): New parameter ALLOC. Change counts to ptrdiff_t. | ||
| 25 | All uses changed. Use push_score to add individual scores; | ||
| 26 | that's simpler than repeating its contents. | ||
| 27 | (score_compare_reverse): Simplify. | ||
| 28 | (push_score): New parameter SIZE. Change counts to ptrdiff_t. | ||
| 29 | All uses changed. Check for integer overflow of size calculation. | ||
| 30 | (sort_scores, write_scores): Change counts to ptrdiff_t. | ||
| 31 | (unlock_file): Preserve errno on success, so that storage | ||
| 32 | exhaustion is diagnosed correctly. | ||
| 33 | |||
| 1 | 2014-01-05 Paul Eggert <eggert@cs.ucla.edu> | 34 | 2014-01-05 Paul Eggert <eggert@cs.ucla.edu> |
| 2 | 35 | ||
| 3 | Spelling fixes. | 36 | Spelling fixes. |
diff --git a/lib-src/update-game-score.c b/lib-src/update-game-score.c index e2bf93bb1eb..916a351432d 100644 --- a/lib-src/update-game-score.c +++ b/lib-src/update-game-score.c | |||
| @@ -35,7 +35,9 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ | |||
| 35 | 35 | ||
| 36 | #include <unistd.h> | 36 | #include <unistd.h> |
| 37 | #include <errno.h> | 37 | #include <errno.h> |
| 38 | #include <inttypes.h> | ||
| 38 | #include <limits.h> | 39 | #include <limits.h> |
| 40 | #include <stdbool.h> | ||
| 39 | #include <string.h> | 41 | #include <string.h> |
| 40 | #include <stdlib.h> | 42 | #include <stdlib.h> |
| 41 | #include <stdio.h> | 43 | #include <stdio.h> |
| @@ -50,8 +52,11 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ | |||
| 50 | #include "ntlib.h" | 52 | #include "ntlib.h" |
| 51 | #endif | 53 | #endif |
| 52 | 54 | ||
| 55 | #ifndef min | ||
| 56 | # define min(a,b) ((a) < (b) ? (a) : (b)) | ||
| 57 | #endif | ||
| 58 | |||
| 53 | #define MAX_ATTEMPTS 5 | 59 | #define MAX_ATTEMPTS 5 |
| 54 | #define MAX_SCORES 200 | ||
| 55 | #define MAX_DATA_LEN 1024 | 60 | #define MAX_DATA_LEN 1024 |
| 56 | 61 | ||
| 57 | #ifndef HAVE_DIFFTIME | 62 | #ifndef HAVE_DIFFTIME |
| @@ -76,18 +81,21 @@ static int unlock_file (const char *filename, void *state); | |||
| 76 | 81 | ||
| 77 | struct score_entry | 82 | struct score_entry |
| 78 | { | 83 | { |
| 79 | long score; | 84 | intmax_t score; |
| 80 | char *username; | 85 | char *username; |
| 81 | char *data; | 86 | char *data; |
| 82 | }; | 87 | }; |
| 83 | 88 | ||
| 89 | #define MAX_SCORES min (PTRDIFF_MAX, SIZE_MAX / sizeof (struct score_entry)) | ||
| 90 | |||
| 84 | static int read_scores (const char *filename, struct score_entry **scores, | 91 | static int read_scores (const char *filename, struct score_entry **scores, |
| 85 | int *count); | 92 | ptrdiff_t *count, ptrdiff_t *alloc); |
| 86 | static int push_score (struct score_entry **scores, int *count, | 93 | static int push_score (struct score_entry **scores, ptrdiff_t *count, |
| 87 | int newscore, char *username, char *newdata); | 94 | ptrdiff_t *size, struct score_entry const *newscore); |
| 88 | static void sort_scores (struct score_entry *scores, int count, int reverse); | 95 | static void sort_scores (struct score_entry *scores, ptrdiff_t count, |
| 96 | bool reverse); | ||
| 89 | static int write_scores (const char *filename, | 97 | static int write_scores (const char *filename, |
| 90 | const struct score_entry *scores, int count); | 98 | const struct score_entry *scores, ptrdiff_t count); |
| 91 | 99 | ||
| 92 | static _Noreturn void | 100 | static _Noreturn void |
| 93 | lose (const char *msg) | 101 | lose (const char *msg) |
| @@ -107,19 +115,19 @@ static char * | |||
| 107 | get_user_id (void) | 115 | get_user_id (void) |
| 108 | { | 116 | { |
| 109 | struct passwd *buf = getpwuid (getuid ()); | 117 | struct passwd *buf = getpwuid (getuid ()); |
| 110 | if (!buf) | 118 | if (!buf || strchr (buf->pw_name, ' ') || strchr (buf->pw_name, '\n')) |
| 111 | { | 119 | { |
| 112 | long uid = getuid (); | 120 | intmax_t uid = getuid (); |
| 113 | char *name = malloc (sizeof uid * CHAR_BIT / 3 + 1); | 121 | char *name = malloc (sizeof uid * CHAR_BIT / 3 + 4); |
| 114 | if (name) | 122 | if (name) |
| 115 | sprintf (name, "%ld", uid); | 123 | sprintf (name, "%"PRIdMAX, uid); |
| 116 | return name; | 124 | return name; |
| 117 | } | 125 | } |
| 118 | return buf->pw_name; | 126 | return buf->pw_name; |
| 119 | } | 127 | } |
| 120 | 128 | ||
| 121 | static const char * | 129 | static const char * |
| 122 | get_prefix (int running_suid, const char *user_prefix) | 130 | get_prefix (bool running_suid, const char *user_prefix) |
| 123 | { | 131 | { |
| 124 | if (!running_suid && user_prefix == NULL) | 132 | if (!running_suid && user_prefix == NULL) |
| 125 | lose ("Not using a shared game directory, and no prefix given."); | 133 | lose ("Not using a shared game directory, and no prefix given."); |
| @@ -137,14 +145,18 @@ get_prefix (int running_suid, const char *user_prefix) | |||
| 137 | int | 145 | int |
| 138 | main (int argc, char **argv) | 146 | main (int argc, char **argv) |
| 139 | { | 147 | { |
| 140 | int c, running_suid; | 148 | int c; |
| 149 | bool running_suid; | ||
| 141 | void *lockstate; | 150 | void *lockstate; |
| 142 | char *user_id, *scorefile; | 151 | char *scorefile; |
| 152 | char *nl; | ||
| 143 | const char *prefix, *user_prefix = NULL; | 153 | const char *prefix, *user_prefix = NULL; |
| 144 | struct stat buf; | 154 | struct stat buf; |
| 145 | struct score_entry *scores; | 155 | struct score_entry *scores; |
| 146 | int newscore, scorecount, reverse = 0, max = MAX_SCORES; | 156 | struct score_entry newscore; |
| 147 | char *newdata; | 157 | bool reverse = false; |
| 158 | ptrdiff_t scorecount, scorealloc; | ||
| 159 | ptrdiff_t max_scores = MAX_SCORES; | ||
| 148 | 160 | ||
| 149 | srand (time (0)); | 161 | srand (time (0)); |
| 150 | 162 | ||
| @@ -161,15 +173,18 @@ main (int argc, char **argv) | |||
| 161 | reverse = 1; | 173 | reverse = 1; |
| 162 | break; | 174 | break; |
| 163 | case 'm': | 175 | case 'm': |
| 164 | max = atoi (optarg); | 176 | { |
| 165 | if (max > MAX_SCORES) | 177 | intmax_t m = strtoimax (optarg, 0, 10); |
| 166 | max = MAX_SCORES; | 178 | if (m < 0) |
| 179 | usage (EXIT_FAILURE); | ||
| 180 | max_scores = min (m, MAX_SCORES); | ||
| 181 | } | ||
| 167 | break; | 182 | break; |
| 168 | default: | 183 | default: |
| 169 | usage (EXIT_FAILURE); | 184 | usage (EXIT_FAILURE); |
| 170 | } | 185 | } |
| 171 | 186 | ||
| 172 | if (optind+3 != argc) | 187 | if (argc - optind != 3) |
| 173 | usage (EXIT_FAILURE); | 188 | usage (EXIT_FAILURE); |
| 174 | 189 | ||
| 175 | running_suid = (getuid () != geteuid ()); | 190 | running_suid = (getuid () != geteuid ()); |
| @@ -183,13 +198,18 @@ main (int argc, char **argv) | |||
| 183 | strcpy (scorefile, prefix); | 198 | strcpy (scorefile, prefix); |
| 184 | strcat (scorefile, "/"); | 199 | strcat (scorefile, "/"); |
| 185 | strcat (scorefile, argv[optind]); | 200 | strcat (scorefile, argv[optind]); |
| 186 | newscore = atoi (argv[optind+1]); | ||
| 187 | newdata = argv[optind+2]; | ||
| 188 | if (strlen (newdata) > MAX_DATA_LEN) | ||
| 189 | newdata[MAX_DATA_LEN] = '\0'; | ||
| 190 | 201 | ||
| 191 | user_id = get_user_id (); | 202 | newscore.score = strtoimax (argv[optind + 1], 0, 10); |
| 192 | if (user_id == NULL) | 203 | |
| 204 | newscore.data = argv[optind + 2]; | ||
| 205 | if (strlen (newscore.data) > MAX_DATA_LEN) | ||
| 206 | newscore.data[MAX_DATA_LEN] = '\0'; | ||
| 207 | nl = strchr (newscore.data, '\n'); | ||
| 208 | if (nl) | ||
| 209 | *nl = '\0'; | ||
| 210 | |||
| 211 | newscore.username = get_user_id (); | ||
| 212 | if (! newscore.username) | ||
| 193 | lose_syserr ("Couldn't determine user id"); | 213 | lose_syserr ("Couldn't determine user id"); |
| 194 | 214 | ||
| 195 | if (stat (scorefile, &buf) < 0) | 215 | if (stat (scorefile, &buf) < 0) |
| @@ -198,29 +218,34 @@ main (int argc, char **argv) | |||
| 198 | if (lock_file (scorefile, &lockstate) < 0) | 218 | if (lock_file (scorefile, &lockstate) < 0) |
| 199 | lose_syserr ("Failed to lock scores file"); | 219 | lose_syserr ("Failed to lock scores file"); |
| 200 | 220 | ||
| 201 | if (read_scores (scorefile, &scores, &scorecount) < 0) | 221 | if (read_scores (scorefile, &scores, &scorecount, &scorealloc) < 0) |
| 202 | { | 222 | { |
| 203 | unlock_file (scorefile, lockstate); | 223 | unlock_file (scorefile, lockstate); |
| 204 | lose_syserr ("Failed to read scores file"); | 224 | lose_syserr ("Failed to read scores file"); |
| 205 | } | 225 | } |
| 206 | push_score (&scores, &scorecount, newscore, user_id, newdata); | 226 | if (push_score (&scores, &scorecount, &scorealloc, &newscore) < 0) |
| 227 | { | ||
| 228 | unlock_file (scorefile, lockstate); | ||
| 229 | lose_syserr ("Failed to add score"); | ||
| 230 | } | ||
| 207 | sort_scores (scores, scorecount, reverse); | 231 | sort_scores (scores, scorecount, reverse); |
| 208 | /* Limit the number of scores. If we're using reverse sorting, then | 232 | /* Limit the number of scores. If we're using reverse sorting, then |
| 209 | also increment the beginning of the array, to skip over the | 233 | also increment the beginning of the array, to skip over the |
| 210 | *smallest* scores. Otherwise, just decrementing the number of | 234 | *smallest* scores. Otherwise, just decrementing the number of |
| 211 | scores suffices, since the smallest is at the end. */ | 235 | scores suffices, since the smallest is at the end. */ |
| 212 | if (scorecount > MAX_SCORES) | 236 | if (scorecount > max_scores) |
| 213 | { | 237 | { |
| 214 | if (reverse) | 238 | if (reverse) |
| 215 | scores += (scorecount - MAX_SCORES); | 239 | scores += scorecount - max_scores; |
| 216 | scorecount = MAX_SCORES; | 240 | scorecount = max_scores; |
| 217 | } | 241 | } |
| 218 | if (write_scores (scorefile, scores, scorecount) < 0) | 242 | if (write_scores (scorefile, scores, scorecount) < 0) |
| 219 | { | 243 | { |
| 220 | unlock_file (scorefile, lockstate); | 244 | unlock_file (scorefile, lockstate); |
| 221 | lose_syserr ("Failed to write scores file"); | 245 | lose_syserr ("Failed to write scores file"); |
| 222 | } | 246 | } |
| 223 | unlock_file (scorefile, lockstate); | 247 | if (unlock_file (scorefile, lockstate) < 0) |
| 248 | lose_syserr ("Failed to unlock scores file"); | ||
| 224 | exit (EXIT_SUCCESS); | 249 | exit (EXIT_SUCCESS); |
| 225 | } | 250 | } |
| 226 | 251 | ||
| @@ -234,8 +259,12 @@ read_score (FILE *f, struct score_entry *score) | |||
| 234 | return 1; | 259 | return 1; |
| 235 | for (score->score = 0; (c = getc (f)) != EOF && isdigit (c); ) | 260 | for (score->score = 0; (c = getc (f)) != EOF && isdigit (c); ) |
| 236 | { | 261 | { |
| 262 | if (INTMAX_MAX / 10 < score->score) | ||
| 263 | return -1; | ||
| 237 | score->score *= 10; | 264 | score->score *= 10; |
| 238 | score->score += (c-48); | 265 | if (INTMAX_MAX - (c - '0') < score->score) |
| 266 | return -1; | ||
| 267 | score->score += c - '0'; | ||
| 239 | } | 268 | } |
| 240 | while ((c = getc (f)) != EOF | 269 | while ((c = getc (f)) != EOF |
| 241 | && isspace (c)) | 270 | && isspace (c)) |
| @@ -254,18 +283,30 @@ read_score (FILE *f, struct score_entry *score) | |||
| 254 | } | 283 | } |
| 255 | #else | 284 | #else |
| 256 | { | 285 | { |
| 257 | int unameread = 0; | 286 | ptrdiff_t unameread = 0; |
| 258 | int unamelen = 30; | 287 | ptrdiff_t unamelen = 30; |
| 259 | char *username = malloc (unamelen); | 288 | char *username = malloc (unamelen); |
| 260 | if (!username) | 289 | if (!username) |
| 261 | return -1; | 290 | return -1; |
| 262 | 291 | ||
| 263 | while ((c = getc (f)) != EOF | 292 | while ((c = getc (f)) != EOF && c != ' ') |
| 264 | && !isspace (c)) | ||
| 265 | { | 293 | { |
| 266 | if (unameread >= unamelen-1) | 294 | if (unameread >= unamelen - 1) |
| 267 | if (!(username = realloc (username, unamelen *= 2))) | 295 | { |
| 268 | return -1; | 296 | ptrdiff_t unamelen_max = min (PTRDIFF_MAX, SIZE_MAX); |
| 297 | if (unamelen <= unamelen_max / 2) | ||
| 298 | unamelen *= 2; | ||
| 299 | else if (unamelen < unamelen_max) | ||
| 300 | unamelen = unamelen_max; | ||
| 301 | else | ||
| 302 | { | ||
| 303 | errno = ENOMEM; | ||
| 304 | return -1; | ||
| 305 | } | ||
| 306 | username = realloc (username, unamelen); | ||
| 307 | if (!username) | ||
| 308 | return -1; | ||
| 309 | } | ||
| 269 | username[unameread] = c; | 310 | username[unameread] = c; |
| 270 | unameread++; | 311 | unameread++; |
| 271 | } | 312 | } |
| @@ -286,8 +327,8 @@ read_score (FILE *f, struct score_entry *score) | |||
| 286 | } | 327 | } |
| 287 | #else | 328 | #else |
| 288 | { | 329 | { |
| 289 | int cur = 0; | 330 | ptrdiff_t cur = 0; |
| 290 | int len = 16; | 331 | ptrdiff_t len = 16; |
| 291 | char *buf = malloc (len); | 332 | char *buf = malloc (len); |
| 292 | if (!buf) | 333 | if (!buf) |
| 293 | return -1; | 334 | return -1; |
| @@ -296,6 +337,11 @@ read_score (FILE *f, struct score_entry *score) | |||
| 296 | { | 337 | { |
| 297 | if (cur >= len-1) | 338 | if (cur >= len-1) |
| 298 | { | 339 | { |
| 340 | if (min (PTRDIFF_MAX, SIZE_MAX) / 2 < len) | ||
| 341 | { | ||
| 342 | errno = ENOMEM; | ||
| 343 | return -1; | ||
| 344 | } | ||
| 299 | if (!(buf = realloc (buf, len *= 2))) | 345 | if (!(buf = realloc (buf, len *= 2))) |
| 300 | return -1; | 346 | return -1; |
| 301 | } | 347 | } |
| @@ -310,35 +356,25 @@ read_score (FILE *f, struct score_entry *score) | |||
| 310 | } | 356 | } |
| 311 | 357 | ||
| 312 | static int | 358 | static int |
| 313 | read_scores (const char *filename, struct score_entry **scores, int *count) | 359 | read_scores (const char *filename, struct score_entry **scores, |
| 360 | ptrdiff_t *count, ptrdiff_t *alloc) | ||
| 314 | { | 361 | { |
| 315 | int readval = -1, scorecount, cursize; | 362 | int readval = -1; |
| 316 | struct score_entry *ret; | 363 | ptrdiff_t scorecount = 0; |
| 364 | ptrdiff_t cursize = 0; | ||
| 365 | struct score_entry *ret = 0; | ||
| 366 | struct score_entry entry; | ||
| 317 | FILE *f = fopen (filename, "r"); | 367 | FILE *f = fopen (filename, "r"); |
| 318 | int retval = -1; | 368 | int retval = -1; |
| 319 | if (!f) | 369 | if (!f) |
| 320 | return -1; | 370 | return -1; |
| 321 | scorecount = 0; | 371 | while ((readval = read_score (f, &entry)) == 0) |
| 322 | cursize = 16; | 372 | if (push_score (&ret, &scorecount, &cursize, &entry) < 0) |
| 323 | ret = (struct score_entry *) malloc (sizeof (struct score_entry) * cursize); | 373 | return -1; |
| 324 | if (ret) | ||
| 325 | { | ||
| 326 | while ((readval = read_score (f, &ret[scorecount])) == 0) | ||
| 327 | { | ||
| 328 | scorecount++; | ||
| 329 | if (scorecount >= cursize) | ||
| 330 | { | ||
| 331 | cursize *= 2; | ||
| 332 | ret = (struct score_entry *) | ||
| 333 | realloc (ret, (sizeof (struct score_entry) * cursize)); | ||
| 334 | if (!ret) | ||
| 335 | break; | ||
| 336 | } | ||
| 337 | } | ||
| 338 | } | ||
| 339 | if (readval > 0) | 374 | if (readval > 0) |
| 340 | { | 375 | { |
| 341 | *count = scorecount; | 376 | *count = scorecount; |
| 377 | *alloc = cursize; | ||
| 342 | *scores = ret; | 378 | *scores = ret; |
| 343 | retval = 0; | 379 | retval = 0; |
| 344 | } | 380 | } |
| @@ -357,40 +393,53 @@ score_compare (const void *a, const void *b) | |||
| 357 | static int | 393 | static int |
| 358 | score_compare_reverse (const void *a, const void *b) | 394 | score_compare_reverse (const void *a, const void *b) |
| 359 | { | 395 | { |
| 360 | const struct score_entry *sa = (const struct score_entry *) a; | 396 | return score_compare (b, a); |
| 361 | const struct score_entry *sb = (const struct score_entry *) b; | ||
| 362 | return (sa->score > sb->score) - (sa->score < sb->score); | ||
| 363 | } | 397 | } |
| 364 | 398 | ||
| 365 | int | 399 | int |
| 366 | push_score (struct score_entry **scores, int *count, int newscore, char *username, char *newdata) | 400 | push_score (struct score_entry **scores, ptrdiff_t *count, ptrdiff_t *size, |
| 401 | struct score_entry const *newscore) | ||
| 367 | { | 402 | { |
| 368 | struct score_entry *newscores | 403 | struct score_entry *newscores = *scores; |
| 369 | = (struct score_entry *) realloc (*scores, | 404 | if (*count == *size) |
| 370 | sizeof (struct score_entry) * ((*count) + 1)); | 405 | { |
| 371 | if (!newscores) | 406 | ptrdiff_t newsize = *size; |
| 372 | return -1; | 407 | if (newsize <= 0) |
| 373 | newscores[*count].score = newscore; | 408 | newsize = 1; |
| 374 | newscores[*count].username = username; | 409 | else if (newsize <= MAX_SCORES / 2) |
| 375 | newscores[*count].data = newdata; | 410 | newsize *= 2; |
| 411 | else if (newsize < MAX_SCORES) | ||
| 412 | newsize = MAX_SCORES; | ||
| 413 | else | ||
| 414 | { | ||
| 415 | errno = ENOMEM; | ||
| 416 | return -1; | ||
| 417 | } | ||
| 418 | newscores = realloc (newscores, sizeof *newscores * newsize); | ||
| 419 | if (!newscores) | ||
| 420 | return -1; | ||
| 421 | *scores = newscores; | ||
| 422 | *size = newsize; | ||
| 423 | } | ||
| 424 | newscores[*count] = *newscore; | ||
| 376 | (*count) += 1; | 425 | (*count) += 1; |
| 377 | *scores = newscores; | ||
| 378 | return 0; | 426 | return 0; |
| 379 | } | 427 | } |
| 380 | 428 | ||
| 381 | static void | 429 | static void |
| 382 | sort_scores (struct score_entry *scores, int count, int reverse) | 430 | sort_scores (struct score_entry *scores, ptrdiff_t count, bool reverse) |
| 383 | { | 431 | { |
| 384 | qsort (scores, count, sizeof (struct score_entry), | 432 | qsort (scores, count, sizeof *scores, |
| 385 | reverse ? score_compare_reverse : score_compare); | 433 | reverse ? score_compare_reverse : score_compare); |
| 386 | } | 434 | } |
| 387 | 435 | ||
| 388 | static int | 436 | static int |
| 389 | write_scores (const char *filename, const struct score_entry *scores, int count) | 437 | write_scores (const char *filename, const struct score_entry *scores, |
| 438 | ptrdiff_t count) | ||
| 390 | { | 439 | { |
| 391 | int fd; | 440 | int fd; |
| 392 | FILE *f; | 441 | FILE *f; |
| 393 | int i; | 442 | ptrdiff_t i; |
| 394 | char *tempfile = malloc (strlen (filename) + strlen (".tempXXXXXX") + 1); | 443 | char *tempfile = malloc (strlen (filename) + strlen (".tempXXXXXX") + 1); |
| 395 | if (!tempfile) | 444 | if (!tempfile) |
| 396 | return -1; | 445 | return -1; |
| @@ -403,8 +452,9 @@ write_scores (const char *filename, const struct score_entry *scores, int count) | |||
| 403 | if (! f) | 452 | if (! f) |
| 404 | return -1; | 453 | return -1; |
| 405 | for (i = 0; i < count; i++) | 454 | for (i = 0; i < count; i++) |
| 406 | if (fprintf (f, "%ld %s %s\n", scores[i].score, scores[i].username, | 455 | if (fprintf (f, "%"PRIdMAX" %s %s\n", |
| 407 | scores[i].data) < 0) | 456 | scores[i].score, scores[i].username, scores[i].data) |
| 457 | < 0) | ||
| 408 | return -1; | 458 | return -1; |
| 409 | fclose (f); | 459 | fclose (f); |
| 410 | if (rename (tempfile, filename) < 0) | 460 | if (rename (tempfile, filename) < 0) |
| @@ -459,10 +509,11 @@ static int | |||
| 459 | unlock_file (const char *filename, void *state) | 509 | unlock_file (const char *filename, void *state) |
| 460 | { | 510 | { |
| 461 | char *lockpath = (char *) state; | 511 | char *lockpath = (char *) state; |
| 462 | int ret = unlink (lockpath); | ||
| 463 | int saved_errno = errno; | 512 | int saved_errno = errno; |
| 513 | int ret = unlink (lockpath); | ||
| 514 | int unlink_errno = errno; | ||
| 464 | free (lockpath); | 515 | free (lockpath); |
| 465 | errno = saved_errno; | 516 | errno = ret < 0 ? unlink_errno : saved_errno; |
| 466 | return ret; | 517 | return ret; |
| 467 | } | 518 | } |
| 468 | 519 | ||