anastasis-cli-common.c (4090B)
1 /* 2 This file is part of Anastasis 3 Copyright (C) 2026 Anastasis SARL 4 5 Anastasis is free software; you can redistribute it and/or modify it under the 6 terms of the GNU Lesser General Public License as published by the Free Software 7 Foundation; either version 3, or (at your option) any later version. 8 9 Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY 10 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. 12 13 You should have received a copy of the GNU Affero General Public License along with 14 Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> 15 */ 16 /** 17 * @file cli/anastasis-cli-common.c 18 * @brief helpers shared by the Anastasis command line tools 19 * @author Christian Grothoff 20 */ 21 22 #include "platform.h" 23 #include <gnunet/gnunet_util_lib.h> 24 #include "anastasis-cli-common.h" 25 26 #ifndef MAP_ANONYMOUS 27 #define MAP_ANONYMOUS MAP_ANON 28 #endif 29 30 31 /** 32 * Report that the input was larger than #ANASTASIS_CLI_MAX_STATE_SIZE. 33 * 34 * @param[out] error error structure to fill in 35 */ 36 static void 37 set_too_big (json_error_t *error) 38 { 39 GNUNET_snprintf (error->text, 40 sizeof (error->text), 41 "input exceeds the limit of %llu bytes", 42 (unsigned long long) ANASTASIS_CLI_MAX_STATE_SIZE); 43 } 44 45 46 json_t * 47 ANASTASIS_CLI_json_load_capped (FILE *f, 48 json_error_t *error) 49 { 50 int fd = fileno (f); 51 struct stat sb; 52 off_t fsize = -1; 53 size_t map_size; 54 size_t len; 55 void *map; 56 json_t *ret; 57 58 memset (error, 59 0, 60 sizeof (*error)); 61 if ( (-1 != fd) && 62 (0 == fstat (fd, 63 &sb)) && 64 (S_ISREG (sb.st_mode)) && 65 (0 == ftello (f)) ) 66 fsize = sb.st_size; 67 if (-1 != fsize) 68 { 69 /* Regular file positioned at the start: the size is known before we touch 70 the data, so the limit is a single comparison and the file itself can 71 serve as the buffer. */ 72 if (fsize > (off_t) ANASTASIS_CLI_MAX_STATE_SIZE) 73 { 74 set_too_big (error); 75 return NULL; 76 } 77 if (0 == fsize) 78 return json_loadb ("", /* mmap() rejects a zero-length mapping */ 79 0, 80 JSON_DECODE_ANY, 81 error); 82 map_size = (size_t) fsize; 83 len = map_size; 84 map = mmap (NULL, 85 map_size, 86 PROT_READ, 87 MAP_PRIVATE, 88 fd, 89 0); 90 } 91 else 92 { 93 /* A pipe or terminal has no size to stat and nothing to map, so read into 94 an anonymous mapping one byte larger than the limit: the pages we never 95 fill cost nothing, and filling the last one means the input is too big. */ 96 map_size = ANASTASIS_CLI_MAX_STATE_SIZE + 1; 97 len = 0; 98 map = mmap (NULL, 99 map_size, 100 PROT_READ | PROT_WRITE, 101 MAP_PRIVATE | MAP_ANONYMOUS, 102 -1, 103 0); 104 } 105 if (MAP_FAILED == map) 106 { 107 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, 108 "mmap"); 109 GNUNET_snprintf (error->text, 110 sizeof (error->text), 111 "failed to map input: %s", 112 strerror (errno)); 113 return NULL; 114 } 115 if (-1 == fsize) 116 { 117 while (len < map_size) 118 { 119 size_t got; 120 121 got = fread ((char *) map + len, 122 1, 123 map_size - len, 124 f); 125 if (0 == got) 126 break; /* EOF or error; ferror() is reported as a parse failure below */ 127 len += got; 128 } 129 if (len == map_size) 130 { 131 GNUNET_break (0 == munmap (map, 132 map_size)); 133 set_too_big (error); 134 return NULL; 135 } 136 } 137 ret = json_loadb (map, 138 len, 139 JSON_DECODE_ANY, 140 error); 141 GNUNET_break (0 == munmap (map, 142 map_size)); 143 return ret; 144 } 145 146 147 /* end of anastasis-cli-common.c */