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