1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Parse command line, get partition information 4 * 5 * Written by Cai Zhiyong <caizhiyong@huawei.com> 6 * 7 */ 8#include <linux/export.h> 9#include <linux/cmdline-parser.h> 10 11static int parse_subpart(struct cmdline_subpart **subpart, char *partdef) 12{ 13 int ret = 0; 14 struct cmdline_subpart *new_subpart; 15 16 *subpart = NULL; 17 18 new_subpart = kzalloc(sizeof(struct cmdline_subpart), GFP_KERNEL); 19 if (!new_subpart) 20 return -ENOMEM; 21 22 if (*partdef == '-') { 23 new_subpart->size = (sector_t)(~0ULL); 24 partdef++; 25 } else { 26 new_subpart->size = (sector_t)memparse(partdef, &partdef); 27 if (new_subpart->size < (sector_t)PAGE_SIZE) { 28 pr_warn("cmdline partition size is invalid."); 29 ret = -EINVAL; 30 goto fail; 31 } 32 } 33 34 if (*partdef == '@') { 35 partdef++; 36 new_subpart->from = (sector_t)memparse(partdef, &partdef); 37 } else { 38 new_subpart->from = (sector_t)(~0ULL); 39 } 40 41 if (*partdef == '(') { 42 int length; 43 char *next = strchr(++partdef, ')'); 44 45 if (!next) { 46 pr_warn("cmdline partition format is invalid."); 47 ret = -EINVAL; 48 goto fail; 49 } 50 51 length = min_t(int, next - partdef, 52 sizeof(new_subpart->name) - 1); 53 strncpy(new_subpart->name, partdef, length); 54 new_subpart->name[length] = '\0'; 55 56 partdef = ++next; 57 } else 58 new_subpart->name[0] = '\0'; 59 60 new_subpart->flags = 0; 61 62 if (!strncmp(partdef, "ro", 2)) { 63 new_subpart->flags |= PF_RDONLY; 64 partdef += 2; 65 } 66 67 if (!strncmp(partdef, "lk", 2)) { 68 new_subpart->flags |= PF_POWERUP_LOCK; 69 partdef += 2; 70 } 71 72 *subpart = new_subpart; 73 return 0; 74fail: 75 kfree(new_subpart); 76 return ret; 77} 78 79static void free_subpart(struct cmdline_parts *parts) 80{ 81 struct cmdline_subpart *subpart; 82 83 while (parts->subpart) { 84 subpart = parts->subpart; 85 parts->subpart = subpart->next_subpart; 86 kfree(subpart); 87 } 88} 89 90static int parse_parts(struct cmdline_parts **parts, const char *bdevdef) 91{ 92 int ret = -EINVAL; 93 char *next; 94 int length; 95 struct cmdline_subpart **next_subpart; 96 struct cmdline_parts *newparts; 97 char buf[BDEVNAME_SIZE + 32 + 4]; 98 99 *parts = NULL; 100 101 newparts = kzalloc(sizeof(struct cmdline_parts), GFP_KERNEL); 102 if (!newparts) 103 return -ENOMEM; 104 105 next = strchr(bdevdef, ':'); 106 if (!next) { 107 pr_warn("cmdline partition has no block device."); 108 goto fail; 109 } 110 111 length = min_t(int, next - bdevdef, sizeof(newparts->name) - 1); 112 strncpy(newparts->name, bdevdef, length); 113 newparts->name[length] = '\0'; 114 newparts->nr_subparts = 0; 115 116 next_subpart = &newparts->subpart; 117 118 while (next && *(++next)) { 119 bdevdef = next; 120 next = strchr(bdevdef, ','); 121 122 length = (!next) ? (sizeof(buf) - 1) : 123 min_t(int, next - bdevdef, sizeof(buf) - 1); 124 125 strncpy(buf, bdevdef, length); 126 buf[length] = '\0'; 127 128 ret = parse_subpart(next_subpart, buf); 129 if (ret) 130 goto fail; 131 132 newparts->nr_subparts++; 133 next_subpart = &(*next_subpart)->next_subpart; 134 } 135 136 if (!newparts->subpart) { 137 pr_warn("cmdline partition has no valid partition."); 138 ret = -EINVAL; 139 goto fail; 140 } 141 142 *parts = newparts; 143 144 return 0; 145fail: 146 free_subpart(newparts); 147 kfree(newparts); 148 return ret; 149} 150 151void cmdline_parts_free(struct cmdline_parts **parts) 152{ 153 struct cmdline_parts *next_parts; 154 155 while (*parts) { 156 next_parts = (*parts)->next_parts; 157 free_subpart(*parts); 158 kfree(*parts); 159 *parts = next_parts; 160 } 161} 162EXPORT_SYMBOL(cmdline_parts_free); 163 164int cmdline_parts_parse(struct cmdline_parts **parts, const char *cmdline) 165{ 166 int ret; 167 char *buf; 168 char *pbuf; 169 char *next; 170 struct cmdline_parts **next_parts; 171 172 *parts = NULL; 173 174 next = pbuf = buf = kstrdup(cmdline, GFP_KERNEL); 175 if (!buf) 176 return -ENOMEM; 177 178 next_parts = parts; 179 180 while (next && *pbuf) { 181 next = strchr(pbuf, ';'); 182 if (next) 183 *next = '\0'; 184 185 ret = parse_parts(next_parts, pbuf); 186 if (ret) 187 goto fail; 188 189 if (next) 190 pbuf = ++next; 191 192 next_parts = &(*next_parts)->next_parts; 193 } 194 195 if (!*parts) { 196 pr_warn("cmdline partition has no valid partition."); 197 ret = -EINVAL; 198 goto fail; 199 } 200 201 ret = 0; 202done: 203 kfree(buf); 204 return ret; 205 206fail: 207 cmdline_parts_free(parts); 208 goto done; 209} 210EXPORT_SYMBOL(cmdline_parts_parse); 211 212struct cmdline_parts *cmdline_parts_find(struct cmdline_parts *parts, 213 const char *bdev) 214{ 215 while (parts && strncmp(bdev, parts->name, sizeof(parts->name))) 216 parts = parts->next_parts; 217 return parts; 218} 219EXPORT_SYMBOL(cmdline_parts_find); 220 221/* 222 * add_part() 223 * 0 success. 224 * 1 can not add so many partitions. 225 */ 226int cmdline_parts_set(struct cmdline_parts *parts, sector_t disk_size, 227 int slot, 228 int (*add_part)(int, struct cmdline_subpart *, void *), 229 void *param) 230{ 231 sector_t from = 0; 232 struct cmdline_subpart *subpart; 233 234 for (subpart = parts->subpart; subpart; 235 subpart = subpart->next_subpart, slot++) { 236 if (subpart->from == (sector_t)(~0ULL)) 237 subpart->from = from; 238 else 239 from = subpart->from; 240 241 if (from >= disk_size) 242 break; 243 244 if (subpart->size > (disk_size - from)) 245 subpart->size = disk_size - from; 246 247 from += subpart->size; 248 249 if (add_part(slot, subpart, param)) 250 break; 251 } 252 253 return slot; 254} 255EXPORT_SYMBOL(cmdline_parts_set); 256