1 /*
2
3 File: lvm.c
4
5 Copyright (C) 2003-2008 Christophe GRENIER <grenier@cgsecurity.org>
6
7 This software is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License along
18 with this program; if not, write the Free Software Foundation, Inc., 51
19 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20
21 */
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <stdio.h>
27 #ifdef HAVE_STDLIB_H
28 #include <stdlib.h>
29 #endif
30 #ifdef HAVE_STRING_H
31 #include <string.h>
32 #endif
33 #include "types.h"
34 #include "common.h"
35 #include "lvm.h"
36 #include "fnctdsk.h"
37 #include "log.h"
38 #include "guid_cpy.h"
39
40 static void set_LVM_info(partition_t *partition);
41 static int test_LVM(disk_t *disk_car, const pv_disk_t *pv, const partition_t *partition,const int verbose, const int dump_ind);
42
43 static void set_LVM2_info(partition_t*partition);
44 static int test_LVM2(disk_t *disk_car, const struct lvm2_label_header *lh, const partition_t *partition, const int verbose, const int dump_ind);
45
check_LVM(disk_t * disk_car,partition_t * partition,const int verbose)46 int check_LVM(disk_t *disk_car,partition_t *partition,const int verbose)
47 {
48 unsigned char *buffer=(unsigned char*)MALLOC(LVM_PV_DISK_SIZE);
49 if(disk_car->pread(disk_car, buffer, LVM_PV_DISK_SIZE, partition->part_offset) != LVM_PV_DISK_SIZE)
50 {
51 free(buffer);
52 return 1;
53 }
54 if(test_LVM(disk_car,(pv_disk_t *)buffer,partition,verbose,0)!=0)
55 {
56 free(buffer);
57 return 1;
58 }
59 set_LVM_info(partition);
60 free(buffer);
61 return 0;
62 }
63
recover_LVM(disk_t * disk_car,const pv_disk_t * pv,partition_t * partition,const int verbose,const int dump_ind)64 int recover_LVM(disk_t *disk_car, const pv_disk_t *pv,partition_t *partition,const int verbose, const int dump_ind)
65 {
66 if(test_LVM(disk_car,pv,partition,verbose,dump_ind)!=0)
67 return 1;
68 set_LVM_info(partition);
69 partition->part_type_i386=P_LVM;
70 partition->part_type_sun=PSUN_LVM;
71 partition->part_type_gpt=GPT_ENT_TYPE_LINUX_LVM;
72 partition->part_size=(uint64_t)le32(pv->pv_size)*disk_car->sector_size;
73 /* pv_uuid is bigger than part_uuid */
74 guid_cpy(&partition->part_uuid, (const efi_guid_t *)&pv->pv_uuid);
75 if(verbose>0)
76 {
77 log_info("part_size %lu\n",(long unsigned)(partition->part_size/disk_car->sector_size));
78 }
79 return 0;
80 }
81
test_LVM(disk_t * disk_car,const pv_disk_t * pv,const partition_t * partition,const int verbose,const int dump_ind)82 static int test_LVM(disk_t *disk_car, const pv_disk_t *pv, const partition_t *partition, const int verbose, const int dump_ind)
83 {
84 if ((memcmp((const char *)pv->id,LVM_ID,sizeof(pv->id)) == 0) && (le16(pv->version) == 1 || le16(pv->version) == 2))
85 {
86 uint32_t size;
87 if(verbose>0 || dump_ind!=0)
88 {
89 log_info("\nLVM magic value at %u/%u/%u\n", offset2cylinder(disk_car,partition->part_offset),offset2head(disk_car,partition->part_offset),offset2sector(disk_car,partition->part_offset));
90 }
91 if(dump_ind!=0)
92 {
93 /* There is a little offset ... */
94 dump_log(pv,DEFAULT_SECTOR_SIZE);
95 }
96 if (le32(pv->pv_size) > LVM_MAX_SIZE)
97 return (1);
98 if (le32(pv->pv_status) != 0 && le32(pv->pv_status) != PV_ACTIVE)
99 return (1);
100 if (le32(pv->pv_allocatable) != 0 && le32(pv->pv_allocatable) != PV_ALLOCATABLE)
101 return (1);
102 if (le32(pv->lv_cur) > MAX_LV)
103 return (1);
104 if (strlen((const char *)pv->vg_name) > NAME_LEN / 2)
105 return (1);
106 size = le32(pv->pe_size) / LVM_MIN_PE_SIZE * LVM_MIN_PE_SIZE;
107 if ((le32(pv->pe_size) != size) ||
108 (le32(pv->pe_size) < LVM_MIN_PE_SIZE) ||
109 (le32(pv->pe_size) > LVM_MAX_PE_SIZE))
110 return (1);
111
112 if (le32(pv->pe_total) > ( pv->pe_on_disk.size / sizeof ( disk_pe_t)))
113 return (1);
114 if (le32(pv->pe_allocated) > le32(pv->pe_total))
115 return (1);
116 return 0;
117 }
118 return 1;
119 }
120
set_LVM_info(partition_t * partition)121 static void set_LVM_info(partition_t *partition)
122 {
123 partition->upart_type=UP_LVM;
124 partition->fsname[0]='\0';
125 partition->info[0]='\0';
126 snprintf(partition->info,sizeof(partition->info),"LVM");
127 }
128
check_LVM2(disk_t * disk_car,partition_t * partition,const int verbose)129 int check_LVM2(disk_t *disk_car,partition_t *partition,const int verbose)
130 {
131 unsigned char *buffer=(unsigned char *)MALLOC(DEFAULT_SECTOR_SIZE);
132 if(disk_car->pread(disk_car, buffer, DEFAULT_SECTOR_SIZE, partition->part_offset + 0x200) != DEFAULT_SECTOR_SIZE)
133 {
134 free(buffer);
135 return 1;
136 }
137 if(test_LVM2(disk_car,(const struct lvm2_label_header *)buffer,partition,verbose,0)!=0)
138 {
139 free(buffer);
140 return 1;
141 }
142 set_LVM2_info(partition);
143 free(buffer);
144 return 0;
145 }
146
recover_LVM2(disk_t * disk_car,const unsigned char * buf,partition_t * partition,const int verbose,const int dump_ind)147 int recover_LVM2(disk_t *disk_car, const unsigned char *buf,partition_t *partition,const int verbose, const int dump_ind)
148 {
149 const struct lvm2_label_header *lh=(const struct lvm2_label_header *)buf;
150 if(test_LVM2(disk_car,lh,partition,verbose,dump_ind)!=0)
151 return 1;
152 set_LVM2_info(partition);
153 partition->part_type_i386=P_LVM;
154 partition->part_type_sun=PSUN_LVM;
155 partition->part_type_gpt=GPT_ENT_TYPE_LINUX_LVM;
156 {
157 const struct lvm2_pv_header *pvhdr;
158 pvhdr=(const struct lvm2_pv_header *) (buf + le32(lh->offset_xl));
159 partition->part_size=le64(pvhdr->device_size_xl);
160 }
161 if(verbose>0)
162 {
163 log_info("part_size %lu\n",(long unsigned)(partition->part_size/disk_car->sector_size));
164 }
165 return 0;
166 }
167
test_LVM2(disk_t * disk_car,const struct lvm2_label_header * lh,const partition_t * partition,const int verbose,const int dump_ind)168 static int test_LVM2(disk_t *disk_car, const struct lvm2_label_header *lh, const partition_t *partition, const int verbose, const int dump_ind)
169 {
170 if (memcmp((const char *)lh->type,LVM2_LABEL,sizeof(lh->type)) == 0)
171 {
172 if(verbose>0 || dump_ind!=0)
173 {
174 log_info("\nLVM2 magic value at %u/%u/%u\n", offset2cylinder(disk_car,partition->part_offset),offset2head(disk_car,partition->part_offset),offset2sector(disk_car,partition->part_offset));
175 }
176 if(le32(lh->offset_xl)>400)
177 return 1;
178 if(dump_ind!=0)
179 {
180 /* There is a little offset ... */
181 dump_log(lh,DEFAULT_SECTOR_SIZE);
182 }
183 return 0;
184 }
185 return 1;
186 }
187
set_LVM2_info(partition_t * partition)188 static void set_LVM2_info(partition_t*partition)
189 {
190 partition->upart_type=UP_LVM2;
191 partition->fsname[0]='\0';
192 partition->info[0]='\0';
193 snprintf(partition->info,sizeof(partition->info),"LVM2");
194 }
195