283 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * fs/partitions/aix.c4 *5 * Copyright (C) 2012-2013 Philippe De Muyter <phdm@macqel.be>6 */7 8#include "check.h"9 10struct lvm_rec {11 char lvm_id[4]; /* "_LVM" */12 char reserved4[16];13 __be32 lvmarea_len;14 __be32 vgda_len;15 __be32 vgda_psn[2];16 char reserved36[10];17 __be16 pp_size; /* log2(pp_size) */18 char reserved46[12];19 __be16 version;20 };21 22struct vgda {23 __be32 secs;24 __be32 usec;25 char reserved8[16];26 __be16 numlvs;27 __be16 maxlvs;28 __be16 pp_size;29 __be16 numpvs;30 __be16 total_vgdas;31 __be16 vgda_size;32 };33 34struct lvd {35 __be16 lv_ix;36 __be16 res2;37 __be16 res4;38 __be16 maxsize;39 __be16 lv_state;40 __be16 mirror;41 __be16 mirror_policy;42 __be16 num_lps;43 __be16 res10[8];44 };45 46struct lvname {47 char name[64];48 };49 50struct ppe {51 __be16 lv_ix;52 unsigned short res2;53 unsigned short res4;54 __be16 lp_ix;55 unsigned short res8[12];56 };57 58struct pvd {59 char reserved0[16];60 __be16 pp_count;61 char reserved18[2];62 __be32 psn_part1;63 char reserved24[8];64 struct ppe ppe[1016];65 };66 67#define LVM_MAXLVS 25668 69/**70 * read_lba(): Read bytes from disk, starting at given LBA71 * @state72 * @lba73 * @buffer74 * @count75 *76 * Description: Reads @count bytes from @state->disk into @buffer.77 * Returns number of bytes read on success, 0 on error.78 */79static size_t read_lba(struct parsed_partitions *state, u64 lba, u8 *buffer,80 size_t count)81{82 size_t totalreadcount = 0;83 84 if (!buffer || lba + count / 512 > get_capacity(state->disk) - 1ULL)85 return 0;86 87 while (count) {88 int copied = 512;89 Sector sect;90 unsigned char *data = read_part_sector(state, lba++, §);91 if (!data)92 break;93 if (copied > count)94 copied = count;95 memcpy(buffer, data, copied);96 put_dev_sector(sect);97 buffer += copied;98 totalreadcount += copied;99 count -= copied;100 }101 return totalreadcount;102}103 104/**105 * alloc_pvd(): reads physical volume descriptor106 * @state107 * @lba108 *109 * Description: Returns pvd on success, NULL on error.110 * Allocates space for pvd and fill it with disk blocks at @lba111 * Notes: remember to free pvd when you're done!112 */113static struct pvd *alloc_pvd(struct parsed_partitions *state, u32 lba)114{115 size_t count = sizeof(struct pvd);116 struct pvd *p;117 118 p = kmalloc(count, GFP_KERNEL);119 if (!p)120 return NULL;121 122 if (read_lba(state, lba, (u8 *) p, count) < count) {123 kfree(p);124 return NULL;125 }126 return p;127}128 129/**130 * alloc_lvn(): reads logical volume names131 * @state132 * @lba133 *134 * Description: Returns lvn on success, NULL on error.135 * Allocates space for lvn and fill it with disk blocks at @lba136 * Notes: remember to free lvn when you're done!137 */138static struct lvname *alloc_lvn(struct parsed_partitions *state, u32 lba)139{140 size_t count = sizeof(struct lvname) * LVM_MAXLVS;141 struct lvname *p;142 143 p = kmalloc(count, GFP_KERNEL);144 if (!p)145 return NULL;146 147 if (read_lba(state, lba, (u8 *) p, count) < count) {148 kfree(p);149 return NULL;150 }151 return p;152}153 154int aix_partition(struct parsed_partitions *state)155{156 int ret = 0;157 Sector sect;158 unsigned char *d;159 u32 pp_bytes_size;160 u32 pp_blocks_size = 0;161 u32 vgda_sector = 0;162 u32 vgda_len = 0;163 int numlvs = 0;164 struct pvd *pvd = NULL;165 struct lv_info {166 unsigned short pps_per_lv;167 unsigned short pps_found;168 unsigned char lv_is_contiguous;169 } *lvip;170 struct lvname *n = NULL;171 172 d = read_part_sector(state, 7, §);173 if (d) {174 struct lvm_rec *p = (struct lvm_rec *)d;175 u16 lvm_version = be16_to_cpu(p->version);176 char tmp[64];177 178 if (lvm_version == 1) {179 int pp_size_log2 = be16_to_cpu(p->pp_size);180 181 pp_bytes_size = 1 << pp_size_log2;182 pp_blocks_size = pp_bytes_size / 512;183 snprintf(tmp, sizeof(tmp),184 " AIX LVM header version %u found\n",185 lvm_version);186 vgda_len = be32_to_cpu(p->vgda_len);187 vgda_sector = be32_to_cpu(p->vgda_psn[0]);188 } else {189 snprintf(tmp, sizeof(tmp),190 " unsupported AIX LVM version %d found\n",191 lvm_version);192 }193 strlcat(state->pp_buf, tmp, PAGE_SIZE);194 put_dev_sector(sect);195 }196 if (vgda_sector && (d = read_part_sector(state, vgda_sector, §))) {197 struct vgda *p = (struct vgda *)d;198 199 numlvs = be16_to_cpu(p->numlvs);200 put_dev_sector(sect);201 }202 lvip = kcalloc(state->limit, sizeof(struct lv_info), GFP_KERNEL);203 if (!lvip)204 return 0;205 if (numlvs && (d = read_part_sector(state, vgda_sector + 1, §))) {206 struct lvd *p = (struct lvd *)d;207 int i;208 209 n = alloc_lvn(state, vgda_sector + vgda_len - 33);210 if (n) {211 int foundlvs = 0;212 213 for (i = 0; foundlvs < numlvs && i < state->limit; i += 1) {214 lvip[i].pps_per_lv = be16_to_cpu(p[i].num_lps);215 if (lvip[i].pps_per_lv)216 foundlvs += 1;217 }218 /* pvd loops depend on n[].name and lvip[].pps_per_lv */219 pvd = alloc_pvd(state, vgda_sector + 17);220 }221 put_dev_sector(sect);222 }223 if (pvd) {224 int numpps = be16_to_cpu(pvd->pp_count);225 int psn_part1 = be32_to_cpu(pvd->psn_part1);226 int i;227 int cur_lv_ix = -1;228 int next_lp_ix = 1;229 int lp_ix;230 231 for (i = 0; i < numpps; i += 1) {232 struct ppe *p = pvd->ppe + i;233 unsigned int lv_ix;234 235 lp_ix = be16_to_cpu(p->lp_ix);236 if (!lp_ix) {237 next_lp_ix = 1;238 continue;239 }240 lv_ix = be16_to_cpu(p->lv_ix) - 1;241 if (lv_ix >= state->limit) {242 cur_lv_ix = -1;243 continue;244 }245 lvip[lv_ix].pps_found += 1;246 if (lp_ix == 1) {247 cur_lv_ix = lv_ix;248 next_lp_ix = 1;249 } else if (lv_ix != cur_lv_ix || lp_ix != next_lp_ix) {250 next_lp_ix = 1;251 continue;252 }253 if (lp_ix == lvip[lv_ix].pps_per_lv) {254 char tmp[70];255 256 put_partition(state, lv_ix + 1,257 (i + 1 - lp_ix) * pp_blocks_size + psn_part1,258 lvip[lv_ix].pps_per_lv * pp_blocks_size);259 snprintf(tmp, sizeof(tmp), " <%s>\n",260 n[lv_ix].name);261 strlcat(state->pp_buf, tmp, PAGE_SIZE);262 lvip[lv_ix].lv_is_contiguous = 1;263 ret = 1;264 next_lp_ix = 1;265 } else266 next_lp_ix += 1;267 }268 for (i = 0; i < state->limit; i += 1)269 if (lvip[i].pps_found && !lvip[i].lv_is_contiguous) {270 char tmp[sizeof(n[i].name) + 1]; // null char271 272 snprintf(tmp, sizeof(tmp), "%s", n[i].name);273 pr_warn("partition %s (%u pp's found) is "274 "not contiguous\n",275 tmp, lvip[i].pps_found);276 }277 kfree(pvd);278 }279 kfree(n);280 kfree(lvip);281 return ret;282}283