1 /*        $NetBSD: pvchange.c,v 1.1.1.2 2009/12/02 00:25:54 haad Exp $          */
2 
3 /*
4  * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
5  * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
6  *
7  * This file is part of LVM2.
8  *
9  * This copyrighted material is made available to anyone wishing to use,
10  * modify, copy, or redistribute it subject to the terms and conditions
11  * of the GNU Lesser General Public License v.2.1.
12  *
13  * You should have received a copy of the GNU Lesser General Public License
14  * along with this program; if not, write to the Free Software Foundation,
15  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16  */
17 
18 #include "tools.h"
19 
20 /* FIXME Locking.  PVs in VG. */
21 
_pvchange_single(struct cmd_context * cmd,struct physical_volume * pv,void * handle __attribute ((unused)))22 static int _pvchange_single(struct cmd_context *cmd, struct physical_volume *pv,
23                                   void *handle __attribute((unused)))
24 {
25           struct volume_group *vg = NULL;
26           const char *vg_name = NULL;
27           struct pv_list *pvl;
28           uint64_t sector;
29           uint32_t orig_pe_alloc_count;
30           /* FIXME Next three only required for format1. */
31           uint32_t orig_pe_count, orig_pe_size;
32           uint64_t orig_pe_start;
33 
34           const char *pv_name = pv_dev_name(pv);
35           const char *tag = NULL;
36           const char *orig_vg_name;
37           char uuid[64] __attribute((aligned(8)));
38 
39           int allocatable = 0;
40           int tagarg = 0;
41           int r = 0;
42 
43           if (arg_count(cmd, addtag_ARG))
44                     tagarg = addtag_ARG;
45           else if (arg_count(cmd, deltag_ARG))
46                     tagarg = deltag_ARG;
47 
48           if (arg_count(cmd, allocatable_ARG))
49                     allocatable = !strcmp(arg_str_value(cmd, allocatable_ARG, "n"),
50                                               "y");
51           else if (tagarg && !(tag = arg_str_value(cmd, tagarg, NULL))) {
52                     log_error("Failed to get tag");
53                     return 0;
54           }
55 
56           /* If in a VG, must change using volume group. */
57           if (!is_orphan(pv)) {
58                     vg_name = pv_vg_name(pv);
59 
60                     log_verbose("Finding volume group %s of physical volume %s",
61                                   vg_name, pv_name);
62                     vg = vg_read_for_update(cmd, vg_name, NULL, 0);
63                     if (vg_read_error(vg)) {
64                               vg_release(vg);
65                               return_0;
66                     }
67 
68                     if (!(pvl = find_pv_in_vg(vg, pv_name))) {
69                               log_error("Unable to find \"%s\" in volume group \"%s\"",
70                                           pv_name, vg->name);
71                               goto out;
72                     }
73                     if (tagarg && !(vg->fid->fmt->features & FMT_TAGS)) {
74                               log_error("Volume group containing %s does not "
75                                           "support tags", pv_name);
76                               goto out;
77                     }
78                     if (arg_count(cmd, uuid_ARG) && lvs_in_vg_activated(vg)) {
79                               log_error("Volume group containing %s has active "
80                                           "logical volumes", pv_name);
81                               goto out;
82                     }
83                     pv = pvl->pv;
84                     if (!archive(vg))
85                               goto out;
86           } else {
87                     if (tagarg) {
88                               log_error("Can't change tag on Physical Volume %s not "
89                                           "in volume group", pv_name);
90                               return 0;
91                     }
92 
93                     vg_name = VG_ORPHANS;
94 
95                     if (!lock_vol(cmd, vg_name, LCK_VG_WRITE)) {
96                               log_error("Can't get lock for orphans");
97                               return 0;
98                     }
99 
100                     if (!(pv = pv_read(cmd, pv_name, NULL, &sector, 1, 0))) {
101                               unlock_vg(cmd, vg_name);
102                               log_error("Unable to read PV \"%s\"", pv_name);
103                               return 0;
104                     }
105           }
106 
107           if (arg_count(cmd, allocatable_ARG)) {
108                     if (is_orphan(pv) &&
109                         !(pv->fmt->features & FMT_ORPHAN_ALLOCATABLE)) {
110                               log_error("Allocatability not supported by orphan "
111                                           "%s format PV %s", pv->fmt->name, pv_name);
112                               goto out;
113                     }
114 
115                     /* change allocatability for a PV */
116                     if (allocatable && (pv_status(pv) & ALLOCATABLE_PV)) {
117                               log_error("Physical volume \"%s\" is already "
118                                           "allocatable", pv_name);
119                               r = 1;
120                               goto out;
121                     }
122 
123                     if (!allocatable && !(pv_status(pv) & ALLOCATABLE_PV)) {
124                               log_error("Physical volume \"%s\" is already "
125                                           "unallocatable", pv_name);
126                               r = 1;
127                               goto out;
128                     }
129 
130                     if (allocatable) {
131                               log_verbose("Setting physical volume \"%s\" "
132                                             "allocatable", pv_name);
133                               pv->status |= ALLOCATABLE_PV;
134                     } else {
135                               log_verbose("Setting physical volume \"%s\" NOT "
136                                             "allocatable", pv_name);
137                               pv->status &= ~ALLOCATABLE_PV;
138                     }
139           } else if (tagarg) {
140                     /* tag or deltag */
141                     if ((tagarg == addtag_ARG)) {
142                               if (!str_list_add(cmd->mem, &pv->tags, tag)) {
143                                         log_error("Failed to add tag %s to physical "
144                                                     "volume %s", tag, pv_name);
145                                         goto out;
146                               }
147                     } else {
148                               if (!str_list_del(&pv->tags, tag)) {
149                                         log_error("Failed to remove tag %s from "
150                                                     "physical volume" "%s", tag, pv_name);
151                                         goto out;
152                               }
153                     }
154           } else {
155                     /* --uuid: Change PV ID randomly */
156                     if (!id_create(&pv->id)) {
157                               log_error("Failed to generate new random UUID for %s.",
158                                           pv_name);
159                               goto out;
160                     }
161                     if (!id_write_format(&pv->id, uuid, sizeof(uuid)))
162                               goto_out;
163                     log_verbose("Changing uuid of %s to %s.", pv_name, uuid);
164                     if (!is_orphan(pv)) {
165                               orig_vg_name = pv_vg_name(pv);
166                               orig_pe_alloc_count = pv_pe_alloc_count(pv);
167 
168                               /* FIXME format1 pv_write doesn't preserve these. */
169                               orig_pe_size = pv_pe_size(pv);
170                               orig_pe_start = pv_pe_start(pv);
171                               orig_pe_count = pv_pe_count(pv);
172 
173                               pv->vg_name = pv->fmt->orphan_vg_name;
174                               pv->pe_alloc_count = 0;
175                               if (!(pv_write(cmd, pv, NULL, INT64_C(-1)))) {
176                                         log_error("pv_write with new uuid failed "
177                                                     "for %s.", pv_name);
178                                         goto out;
179                               }
180                               pv->vg_name = orig_vg_name;
181                               pv->pe_alloc_count = orig_pe_alloc_count;
182 
183                               pv->pe_size = orig_pe_size;
184                               pv->pe_start = orig_pe_start;
185                               pv->pe_count = orig_pe_count;
186                     }
187           }
188 
189           log_verbose("Updating physical volume \"%s\"", pv_name);
190           if (!is_orphan(pv)) {
191                     if (!vg_write(vg) || !vg_commit(vg)) {
192                               log_error("Failed to store physical volume \"%s\" in "
193                                           "volume group \"%s\"", pv_name, vg->name);
194                               goto out;
195                     }
196                     backup(vg);
197           } else if (!(pv_write(cmd, pv, NULL, INT64_C(-1)))) {
198                     log_error("Failed to store physical volume \"%s\"",
199                                 pv_name);
200                     goto out;
201           }
202 
203           log_print("Physical volume \"%s\" changed", pv_name);
204           r = 1;
205 out:
206           unlock_and_release_vg(cmd, vg, vg_name);
207           return r;
208 
209 }
210 
pvchange(struct cmd_context * cmd,int argc,char ** argv)211 int pvchange(struct cmd_context *cmd, int argc, char **argv)
212 {
213           int opt = 0;
214           int done = 0;
215           int total = 0;
216 
217           struct physical_volume *pv;
218           char *pv_name;
219 
220           struct pv_list *pvl;
221           struct dm_list *pvslist;
222           struct dm_list mdas;
223 
224           if (arg_count(cmd, allocatable_ARG) + arg_count(cmd, addtag_ARG) +
225               arg_count(cmd, deltag_ARG) + arg_count(cmd, uuid_ARG) != 1) {
226                     log_error("Please give exactly one option of -x, -uuid, "
227                                 "--addtag or --deltag");
228                     return EINVALID_CMD_LINE;
229           }
230 
231           if (!(arg_count(cmd, all_ARG)) && !argc) {
232                     log_error("Please give a physical volume path");
233                     return EINVALID_CMD_LINE;
234           }
235 
236           if (arg_count(cmd, all_ARG) && argc) {
237                     log_error("Option a and PhysicalVolumePath are exclusive");
238                     return EINVALID_CMD_LINE;
239           }
240 
241           if (argc) {
242                     log_verbose("Using physical volume(s) on command line");
243                     for (; opt < argc; opt++) {
244                               pv_name = argv[opt];
245                               dm_list_init(&mdas);
246                               if (!(pv = pv_read(cmd, pv_name, &mdas, NULL, 1, 0))) {
247                                         log_error("Failed to read physical volume %s",
248                                                     pv_name);
249                                         continue;
250                               }
251                               /*
252                                * If a PV has no MDAs it may appear to be an
253                                * orphan until the metadata is read off
254                                * another PV in the same VG.  Detecting this
255                                * means checking every VG by scanning every
256                                * PV on the system.
257                                */
258                               if (is_orphan(pv) && !dm_list_size(&mdas)) {
259                                         if (!scan_vgs_for_pvs(cmd)) {
260                                                   log_error("Rescan for PVs without "
261                                                               "metadata areas failed.");
262                                                   continue;
263                                         }
264                                         if (!(pv = pv_read(cmd, pv_name,
265                                                                NULL, NULL, 1, 0))) {
266                                                   log_error("Failed to read "
267                                                               "physical volume %s",
268                                                               pv_name);
269                                                   continue;
270                                         }
271                               }
272 
273                               total++;
274                               done += _pvchange_single(cmd, pv, NULL);
275                     }
276           } else {
277                     log_verbose("Scanning for physical volume names");
278                     if (!(pvslist = get_pvs(cmd))) {
279                               stack;
280                               return ECMD_FAILED;
281                     }
282 
283                     dm_list_iterate_items(pvl, pvslist) {
284                               total++;
285                               done += _pvchange_single(cmd, pvl->pv, NULL);
286                     }
287           }
288 
289           log_print("%d physical volume%s changed / %d physical volume%s "
290                       "not changed",
291                       done, done == 1 ? "" : "s",
292                       total - done, (total - done) == 1 ? "" : "s");
293 
294           return (total == done) ? ECMD_PROCESSED : ECMD_FAILED;
295 }
296