Line 0
Link Here
|
|
|
1 |
/* |
2 |
* (C) Copyright IBM Corp. 2001, 2003 |
3 |
* |
4 |
* This program is free software; you can redistribute it and/or modify |
5 |
* it under the terms of the GNU General Public License as published by |
6 |
* the Free Software Foundation; either version 2 of the License, or |
7 |
* (at your option) any later version. |
8 |
* |
9 |
* This program is distributed in the hope that it will be useful, |
10 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See |
12 |
* the GNU General Public License for more details. |
13 |
* |
14 |
* You should have received a copy of the GNU General Public License |
15 |
* along with this program; if not, write to the Free Software |
16 |
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
17 |
* |
18 |
* Local Disk Manager plugin. |
19 |
*/ |
20 |
|
21 |
#define _GNU_SOURCE |
22 |
|
23 |
#include <stdlib.h> |
24 |
#include <stdio.h> |
25 |
#include <string.h> |
26 |
#include <fcntl.h> |
27 |
#include <mntent.h> |
28 |
#include <dirent.h> |
29 |
#include <unistd.h> |
30 |
#include <sys/ioctl.h> |
31 |
#include <sys/stat.h> |
32 |
#include <sys/types.h> |
33 |
#include <errno.h> |
34 |
#include <glob.h> |
35 |
#include <malloc.h> |
36 |
#include <wait.h> |
37 |
|
38 |
#include <plugin.h> |
39 |
#include <ldm_funcs.h> |
40 |
#include "localdskmgr.h" |
41 |
#include "cache.h" |
42 |
#include "info.h" |
43 |
|
44 |
engine_functions_t * EngFncs = NULL; |
45 |
|
46 |
char * base_directory = NULL; |
47 |
int base_directory_len; |
48 |
char * sysfs_mount_point; |
49 |
const char * scan; |
50 |
const char * const * directories; |
51 |
int directories_count; |
52 |
const char * const * includes; |
53 |
int include_count; |
54 |
const char * const default_legacy_includes[] = { |
55 |
"sd?", |
56 |
"hd?", |
57 |
"dasd?" |
58 |
}; |
59 |
const char * const default_sysfs_includes[] = { |
60 |
"*?", |
61 |
}; |
62 |
const char * const * excludes; |
63 |
int exclude_count; |
64 |
glob_t dev_names_glob; |
65 |
int glob_flags; |
66 |
static char pattern[PATH_MAX]; |
67 |
|
68 |
static dm_device_list_t * dm_devices = NULL; |
69 |
static list_anchor_t multipath_children = NULL; |
70 |
|
71 |
/* Global list for keeping track of open file-handles. On systems with |
72 |
* a large number of disks, we don't want to keep open file-handles to |
73 |
* every disk, or the process may run out of available file-handles. |
74 |
*/ |
75 |
static list_anchor_t file_handles = NULL; |
76 |
u_int32_t num_file_handles = 0; |
77 |
#define DEFAULT_FILE_HANDLES 64 |
78 |
#define MAX_FILE_HANDLES 1024 |
79 |
|
80 |
static void close_dev(storage_object_t * disk); |
81 |
|
82 |
/** |
83 |
* round_down |
84 |
* @value: Value (in sectors) to be rounded down. |
85 |
* @boundary: Boundary (in bytes) to round-down to. |
86 |
* |
87 |
* Given a value, round it down to be a multiple of the specified boundary size. |
88 |
**/ |
89 |
static inline sector_count_t round_down(sector_count_t value, |
90 |
u_int32_t boundary) |
91 |
{ |
92 |
sector_count_t boundary_in_vsectors = ((sector_count_t)boundary) >> |
93 |
EVMS_VSECTOR_SIZE_SHIFT; |
94 |
return (boundary > EVMS_VSECTOR_SIZE) ? |
95 |
(value & ~(boundary_in_vsectors - 1)) : value; |
96 |
} |
97 |
|
98 |
/** |
99 |
* round_up |
100 |
* @value: Value (in sectors) to be rounded up. |
101 |
* @boundary: Boundary (in bytes) to round-up to. |
102 |
* |
103 |
* Given a value, round it up to be a multiple of the specified boundary size. |
104 |
**/ |
105 |
static inline sector_count_t round_up(sector_count_t value, |
106 |
u_int32_t boundary) |
107 |
{ |
108 |
sector_count_t boundary_in_vsectors = ((sector_count_t)boundary) >> |
109 |
EVMS_VSECTOR_SIZE_SHIFT; |
110 |
sector_count_t temp_value = value + boundary_in_vsectors - 1; |
111 |
return (boundary > EVMS_VSECTOR_SIZE) ? |
112 |
(temp_value & ~(boundary_in_vsectors - 1)) : value; |
113 |
} |
114 |
|
115 |
/** |
116 |
* drbd_active |
117 |
* |
118 |
* Is the drbd driver running? If so, the nbd driver cannot also be running, |
119 |
* since they use the same major-number. |
120 |
**/ |
121 |
static int drbd_active = -1; |
122 |
|
123 |
static boolean is_drbd_active(void) |
124 |
{ |
125 |
struct stat st; |
126 |
int rc; |
127 |
|
128 |
LOG_ENTRY(); |
129 |
|
130 |
if (drbd_active == -1) { |
131 |
rc = stat("/proc/drbd", &st); |
132 |
if (rc) { |
133 |
drbd_active = FALSE; |
134 |
} else { |
135 |
drbd_active = TRUE; |
136 |
} |
137 |
} |
138 |
|
139 |
LOG_EXIT_BOOL(drbd_active); |
140 |
return drbd_active; |
141 |
} |
142 |
|
143 |
static boolean search_mount_records(FILE * records, |
144 |
char * fs_name, |
145 |
char ** mount_name) |
146 |
{ |
147 |
boolean found = FALSE; |
148 |
struct mntent * mount_entry; |
149 |
|
150 |
LOG_ENTRY(); |
151 |
|
152 |
while (!found && (mount_entry = getmntent(records)) != NULL) { |
153 |
if (strcmp(mount_entry->mnt_type, fs_name) == 0) { |
154 |
found = TRUE; |
155 |
if (mount_name) { |
156 |
*mount_name = strdup(mount_entry->mnt_dir); |
157 |
} |
158 |
} |
159 |
} |
160 |
|
161 |
LOG_EXIT_BOOL(found); |
162 |
return found; |
163 |
} |
164 |
|
165 |
/** |
166 |
* where_is_sysfs |
167 |
* |
168 |
* Is sysfs mounted. If so, return the mount point. The caller must free the |
169 |
* returned string. |
170 |
**/ |
171 |
static boolean where_is_sysfs(char ** mount_name) |
172 |
{ |
173 |
boolean found = FALSE; |
174 |
FILE * mount_records; |
175 |
|
176 |
LOG_ENTRY(); |
177 |
|
178 |
if (mount_name) { |
179 |
*mount_name = NULL; |
180 |
} |
181 |
|
182 |
mount_records = setmntent(MOUNTED, "r"); |
183 |
if (mount_records) { |
184 |
LOG_DEBUG("Searching for sysfs in %s.\n", MOUNTED); |
185 |
found = search_mount_records(mount_records, "sysfs", mount_name); |
186 |
endmntent(mount_records); |
187 |
} |
188 |
|
189 |
if (!found) { |
190 |
mount_records = setmntent("/proc/mounts", "r"); |
191 |
if (mount_records) { |
192 |
LOG_DEBUG("Searching for sysfs in /proc/mounts.\n"); |
193 |
found = search_mount_records(mount_records, "sysfs", mount_name); |
194 |
endmntent(mount_records); |
195 |
} |
196 |
} |
197 |
|
198 |
LOG_EXIT_BOOL(found); |
199 |
return found; |
200 |
} |
201 |
|
202 |
static void get_legacy_config() |
203 |
{ |
204 |
LOG_ENTRY(); |
205 |
|
206 |
scan = "/dev/"; |
207 |
EngFncs->get_config_string("legacy_devices.scan", &scan); |
208 |
|
209 |
directories_count = 0; |
210 |
directories = NULL; |
211 |
EngFncs->get_config_string_array("legacy_devices.directories", |
212 |
&directories_count, &directories); |
213 |
include_count = 0; |
214 |
includes = NULL; |
215 |
EngFncs->get_config_string_array("legacy_devices.include", |
216 |
&include_count, &includes); |
217 |
if (includes == NULL) { |
218 |
includes = default_legacy_includes; |
219 |
include_count = sizeof(default_legacy_includes) / |
220 |
sizeof(default_legacy_includes[0]); |
221 |
} |
222 |
|
223 |
exclude_count = 0; |
224 |
excludes = NULL; |
225 |
EngFncs->get_config_string_array("legacy_devices.exclude", |
226 |
&exclude_count, &excludes); |
227 |
|
228 |
EngFncs->get_config_uint32("legacy_devices.max_open_disks", |
229 |
&num_file_handles); |
230 |
|
231 |
LOG_EXIT_VOID(); |
232 |
} |
233 |
|
234 |
static void get_sysfs_config() |
235 |
{ |
236 |
boolean ignore_sysfs = FALSE; |
237 |
|
238 |
LOG_ENTRY(); |
239 |
|
240 |
EngFncs->get_config_bool("sysfs_devices.ignore_sysfs", &ignore_sysfs); |
241 |
if (ignore_sysfs) { |
242 |
/* Fall back to the legacy_devices section |
243 |
* if the config file says to ignore sysfs. |
244 |
*/ |
245 |
free(sysfs_mount_point); |
246 |
sysfs_mount_point = NULL; |
247 |
get_legacy_config(); |
248 |
LOG_EXIT_VOID(); |
249 |
return; |
250 |
} |
251 |
|
252 |
include_count = 0; |
253 |
includes = NULL; |
254 |
EngFncs->get_config_string_array("sysfs_devices.include", |
255 |
&include_count, &includes); |
256 |
if (includes == NULL) { |
257 |
includes = default_sysfs_includes; |
258 |
include_count = sizeof(default_sysfs_includes) / |
259 |
sizeof(default_sysfs_includes[0]); |
260 |
} |
261 |
|
262 |
exclude_count = 0; |
263 |
excludes = NULL; |
264 |
EngFncs->get_config_string_array("sysfs_devices.exclude", |
265 |
&exclude_count, &excludes); |
266 |
|
267 |
EngFncs->get_config_uint32("sysfs_devices.max_open_disks", |
268 |
&num_file_handles); |
269 |
|
270 |
LOG_EXIT_VOID(); |
271 |
} |
272 |
|
273 |
/** |
274 |
* file_handle_cleanup |
275 |
* |
276 |
* Delete the list of file-handle tracking structures. |
277 |
**/ |
278 |
static void file_handle_cleanup(void) |
279 |
{ |
280 |
file_handle_t *handle; |
281 |
list_element_t iter; |
282 |
|
283 |
LOG_ENTRY(); |
284 |
|
285 |
LIST_FOR_EACH(file_handles, iter, handle) { |
286 |
EngFncs->engine_free(handle); |
287 |
} |
288 |
|
289 |
EngFncs->destroy_list(file_handles); |
290 |
file_handles = NULL; |
291 |
|
292 |
LOG_EXIT_VOID(); |
293 |
} |
294 |
|
295 |
/** |
296 |
* file_handle_setup |
297 |
* |
298 |
* Create and initialize MAX_FILE_HANDLES entries on the file_handles list. |
299 |
**/ |
300 |
static int file_handle_setup(void) |
301 |
{ |
302 |
file_handle_t *handle; |
303 |
u_int32_t i; |
304 |
int rc = 0; |
305 |
|
306 |
LOG_ENTRY(); |
307 |
|
308 |
/* Make sure the number of entries in the |
309 |
* file-handles list will be reasonable. |
310 |
*/ |
311 |
if (num_file_handles == 0) { |
312 |
num_file_handles = DEFAULT_FILE_HANDLES; |
313 |
} else if (num_file_handles > MAX_FILE_HANDLES) { |
314 |
num_file_handles = MAX_FILE_HANDLES; |
315 |
} |
316 |
|
317 |
LOG_DEBUG("Allocating %u entries in the file-handles list.\n", |
318 |
num_file_handles); |
319 |
|
320 |
file_handles = EngFncs->allocate_list(); |
321 |
if (!file_handles) { |
322 |
rc = ENOMEM; |
323 |
goto out; |
324 |
} |
325 |
|
326 |
for (i = 0; i < num_file_handles; i++) { |
327 |
handle = EngFncs->engine_alloc(sizeof(*handle)); |
328 |
if (!handle) { |
329 |
file_handle_cleanup(); |
330 |
rc = ENOMEM; |
331 |
break; |
332 |
} |
333 |
handle->elem = EngFncs->insert_thing(file_handles, handle, |
334 |
INSERT_AFTER, NULL); |
335 |
} |
336 |
|
337 |
out: |
338 |
LOG_EXIT_INT(rc); |
339 |
return rc; |
340 |
} |
341 |
|
342 |
/** |
343 |
* file_handle_release |
344 |
* |
345 |
* Release this file-handle back to the pool of available handles. Move it |
346 |
* to the start of the list so it will be quicker to find on the next search. |
347 |
**/ |
348 |
static void file_handle_release(file_handle_t *handle) |
349 |
{ |
350 |
LOG_ENTRY(); |
351 |
|
352 |
handle->disk = NULL; |
353 |
EngFncs->remove_element(handle->elem); |
354 |
EngFncs->insert_element(file_handles, handle->elem, INSERT_BEFORE, NULL); |
355 |
|
356 |
LOG_EXIT_VOID(); |
357 |
} |
358 |
|
359 |
/** |
360 |
* file_handle_make_last |
361 |
* |
362 |
* Move this file-handle to the end of the list so it will be the least likely |
363 |
* to be "stolen". |
364 |
**/ |
365 |
static void file_handle_make_last(file_handle_t *handle) |
366 |
{ |
367 |
LOG_ENTRY(); |
368 |
|
369 |
EngFncs->remove_element(handle->elem); |
370 |
EngFncs->insert_element(file_handles, handle->elem, INSERT_AFTER, NULL); |
371 |
|
372 |
LOG_EXIT_VOID(); |
373 |
} |
374 |
|
375 |
/** |
376 |
* file_handle_find_free |
377 |
* |
378 |
* Search the file-handles list for an unused entry and assign it to this disk. |
379 |
**/ |
380 |
static file_handle_t *file_handle_find_free(void) |
381 |
{ |
382 |
file_handle_t *handle; |
383 |
list_element_t iter; |
384 |
|
385 |
LOG_ENTRY(); |
386 |
|
387 |
LIST_FOR_EACH(file_handles, iter, handle) { |
388 |
if (!handle->disk) { |
389 |
break; |
390 |
} |
391 |
} |
392 |
|
393 |
LOG_EXIT_PTR(handle); |
394 |
return handle; |
395 |
} |
396 |
|
397 |
/** |
398 |
* file_handle_steal_first |
399 |
* |
400 |
* "Steal" the first file-handle on the list, forceably close the disk that |
401 |
* currently owns it, and assign it to the new disk. |
402 |
**/ |
403 |
static file_handle_t *file_handle_steal_first(void) |
404 |
{ |
405 |
file_handle_t *handle; |
406 |
|
407 |
LOG_ENTRY(); |
408 |
|
409 |
handle = EngFncs->first_thing(file_handles, NULL); |
410 |
if (handle->disk) { |
411 |
LOG_DEBUG("Stealing file-handle from disk %s.\n", |
412 |
handle->disk->name); |
413 |
close_dev(handle->disk); |
414 |
} |
415 |
|
416 |
LOG_EXIT_PTR(handle); |
417 |
return handle; |
418 |
} |
419 |
|
420 |
/** |
421 |
* file_handle_get |
422 |
* |
423 |
* Get a file-handle for this disk. First search the list for an used one. If |
424 |
* we can't find one, simply "steal" the first one on the list. |
425 |
**/ |
426 |
static file_handle_t *file_handle_get(void) |
427 |
{ |
428 |
file_handle_t *handle; |
429 |
|
430 |
LOG_ENTRY(); |
431 |
|
432 |
handle = file_handle_find_free(); |
433 |
if (!handle) { |
434 |
handle = file_handle_steal_first(); |
435 |
} |
436 |
|
437 |
LOG_EXIT_PTR(handle); |
438 |
return handle; |
439 |
} |
440 |
|
441 |
static int LD_setup(engine_functions_t * engine_function_table) |
442 |
{ |
443 |
int rc; |
444 |
|
445 |
/* save info we get from the engine */ |
446 |
EngFncs = engine_function_table; |
447 |
|
448 |
LOG_ENTRY(); |
449 |
|
450 |
if (where_is_sysfs(&sysfs_mount_point)) { |
451 |
get_sysfs_config(); |
452 |
} else { |
453 |
get_legacy_config(); |
454 |
} |
455 |
|
456 |
rc = file_handle_setup(); |
457 |
|
458 |
LOG_EXIT_INT(rc); |
459 |
return rc; |
460 |
} |
461 |
|
462 |
/** |
463 |
* open_dev |
464 |
* |
465 |
* Open the specified disk. Use O_DIRECT to avoid caching. Use O_SYNC in case |
466 |
* the kernel does not honor O_DIRECT. Use the Engine's service so we |
467 |
* automatically get a dev-node in the /dev/evms/.nodes/ tree. Record the |
468 |
* file handle in the disk's private data. |
469 |
**/ |
470 |
static int open_dev(storage_object_t * disk) |
471 |
{ |
472 |
local_disk_t * ld = disk->private_data; |
473 |
int rc = 0; |
474 |
|
475 |
LOG_ENTRY(); |
476 |
|
477 |
if (ld->fd <= 0) { |
478 |
ld->file_handle = file_handle_get(); |
479 |
ld->file_handle->disk = disk; |
480 |
|
481 |
ld->fd = EngFncs->open_object(disk, O_RDWR | O_DIRECT | O_SYNC); |
482 |
if (ld->fd < 0) { |
483 |
rc = - ld->fd; |
484 |
file_handle_release(ld->file_handle); |
485 |
ld->file_handle = NULL; |
486 |
LOG_DEBUG("Error opening disk %s: %d: %s\n", |
487 |
disk->name, rc, strerror(rc)); |
488 |
} |
489 |
} |
490 |
|
491 |
if (!rc) { |
492 |
file_handle_make_last(ld->file_handle); |
493 |
} |
494 |
|
495 |
LOG_EXIT_INT(rc); |
496 |
return rc; |
497 |
} |
498 |
|
499 |
/** |
500 |
* close_dev |
501 |
* |
502 |
* Close the disk and clear the file handle. |
503 |
**/ |
504 |
static void close_dev(storage_object_t * disk) |
505 |
{ |
506 |
local_disk_t * ld = disk->private_data; |
507 |
int rc; |
508 |
|
509 |
LOG_ENTRY(); |
510 |
|
511 |
if (ld->fd >= 0) { |
512 |
rc = EngFncs->close_object(disk, ld->fd); |
513 |
file_handle_release(ld->file_handle); |
514 |
ld->file_handle = NULL; |
515 |
ld->fd = -1; |
516 |
} |
517 |
|
518 |
LOG_EXIT_VOID(); |
519 |
} |
520 |
|
521 |
/** |
522 |
* LD_cleanup |
523 |
* |
524 |
* Find any disks and close the device that was opended during discovery. |
525 |
**/ |
526 |
static void LD_cleanup(void) |
527 |
{ |
528 |
storage_object_t * disk; |
529 |
list_anchor_t disk_list; |
530 |
list_element_t disk_list_itr; |
531 |
int rc; |
532 |
|
533 |
LOG_ENTRY(); |
534 |
|
535 |
/* Get a list of disks that are managed by this plug-in. */ |
536 |
rc = EngFncs->get_object_list(DISK, 0, my_plugin_record, |
537 |
NULL, 0, &disk_list); |
538 |
if (!rc) { |
539 |
/* Close any dev handles that might be open. */ |
540 |
LIST_FOR_EACH(disk_list, disk_list_itr, disk) { |
541 |
close_dev(disk); |
542 |
EngFncs->engine_free(disk->private_data); |
543 |
} |
544 |
EngFncs->destroy_list(disk_list); |
545 |
} |
546 |
|
547 |
destroy_cache(); |
548 |
file_handle_cleanup(); |
549 |
|
550 |
if (base_directory) { |
551 |
free(base_directory); |
552 |
base_directory = NULL; |
553 |
} |
554 |
if (sysfs_mount_point) { |
555 |
free(sysfs_mount_point); |
556 |
sysfs_mount_point = NULL; |
557 |
} |
558 |
|
559 |
LOG_EXIT_VOID(); |
560 |
} |
561 |
|
562 |
static void filter_out_excludes(char * pattern, int path_len, int new_globs_index) |
563 |
{ |
564 |
int rc; |
565 |
int i; |
566 |
glob_t exclude_glob = {0}; |
567 |
|
568 |
LOG_ENTRY(); |
569 |
|
570 |
for (i = 0; i < exclude_count; i++) { |
571 |
|
572 |
strcpy(pattern + path_len, excludes[i]); |
573 |
|
574 |
rc = glob(pattern, glob_flags, NULL, &exclude_glob); |
575 |
|
576 |
if (rc == 0) { |
577 |
glob_flags |= GLOB_APPEND; |
578 |
|
579 |
} else { |
580 |
if (rc != GLOB_NOMATCH) { |
581 |
LOG_WARNING("glob() of pattern %s failed with error %s\n", pattern, |
582 |
(rc == GLOB_NOSPACE) ? "GLOB_NOSPACE" : |
583 |
(rc == GLOB_ABEND) ? "GLOB_ABEND" : |
584 |
"(unknown)"); |
585 |
} |
586 |
} |
587 |
} |
588 |
|
589 |
for (i = 0; i < exclude_glob.gl_pathc; i++) { |
590 |
int j; |
591 |
|
592 |
for (j = new_globs_index; j < dev_names_glob.gl_pathc; j++) { |
593 |
if (strcmp(exclude_glob.gl_pathv[i], dev_names_glob.gl_pathv[j]) == 0) { |
594 |
int k; |
595 |
|
596 |
LOG_DEBUG("Removing %s.\n", dev_names_glob.gl_pathv[j]); |
597 |
free(dev_names_glob.gl_pathv[j]); |
598 |
|
599 |
/* Scoot up all following entries. */ |
600 |
for (k = j+1; k < dev_names_glob.gl_pathc; k++) { |
601 |
dev_names_glob.gl_pathv[k-1] = dev_names_glob.gl_pathv[k]; |
602 |
} |
603 |
dev_names_glob.gl_pathc--; |
604 |
|
605 |
break; |
606 |
} |
607 |
} |
608 |
} |
609 |
|
610 |
if (exclude_glob.gl_pathc >= 0) { |
611 |
globfree(&exclude_glob); |
612 |
} |
613 |
|
614 |
LOG_EXIT_VOID(); |
615 |
} |
616 |
|
617 |
static void filter_out_non_block_devices(int new_globs_index) |
618 |
{ |
619 |
int i; |
620 |
struct stat statbuf; |
621 |
int status; |
622 |
|
623 |
LOG_ENTRY(); |
624 |
|
625 |
i = new_globs_index; |
626 |
while (i < dev_names_glob.gl_pathc) { |
627 |
|
628 |
status = stat(dev_names_glob.gl_pathv[i], &statbuf); |
629 |
|
630 |
if (status == 0) { |
631 |
if (!S_ISBLK(statbuf.st_mode)) { |
632 |
int j; |
633 |
|
634 |
LOG_DEBUG("Removing %s.\n", dev_names_glob.gl_pathv[i]); |
635 |
free(dev_names_glob.gl_pathv[i]); |
636 |
|
637 |
/* Scoot up all following entries. */ |
638 |
for (j = i+1; j < dev_names_glob.gl_pathc; j++) { |
639 |
dev_names_glob.gl_pathv[j-1] = dev_names_glob.gl_pathv[j]; |
640 |
} |
641 |
dev_names_glob.gl_pathc--; |
642 |
dev_names_glob.gl_pathv[dev_names_glob.gl_pathc] = NULL; |
643 |
|
644 |
/* Leave "i" as it is so we check the new |
645 |
* entry at the current index. |
646 |
*/ |
647 |
continue; |
648 |
} |
649 |
|
650 |
} else { |
651 |
LOG_WARNING("stat(%s) failed with error code %d: %s\n", dev_names_glob.gl_pathv[i], errno, strerror(errno)); |
652 |
} |
653 |
|
654 |
i++; |
655 |
} |
656 |
|
657 |
LOG_EXIT_VOID(); |
658 |
} |
659 |
|
660 |
static void get_dev_names(const char * dir) |
661 |
{ |
662 |
int rc; |
663 |
int i; |
664 |
int path_len; |
665 |
int new_globs_index; |
666 |
|
667 |
LOG_ENTRY(); |
668 |
LOG_DEBUG("Get device names in directory %s\n", dir); |
669 |
|
670 |
strcpy(pattern, dir); |
671 |
path_len = strlen(pattern); |
672 |
if (pattern[path_len-1] != '/') { |
673 |
pattern[path_len] = '/'; |
674 |
pattern[path_len+1] = '\0'; |
675 |
path_len++; |
676 |
} |
677 |
|
678 |
new_globs_index = dev_names_glob.gl_pathc; |
679 |
|
680 |
for (i = 0; i < include_count; i++) { |
681 |
|
682 |
strcpy(pattern + path_len, includes[i]); |
683 |
|
684 |
rc = glob(pattern, glob_flags, NULL, &dev_names_glob); |
685 |
|
686 |
if (rc == 0) { |
687 |
glob_flags |= GLOB_APPEND; |
688 |
|
689 |
} else { |
690 |
if (rc != GLOB_NOMATCH) { |
691 |
LOG_WARNING("glob() of pattern %s failed with error %s\n", pattern, |
692 |
(rc == GLOB_NOSPACE) ? "GLOB_NOSPACE" : |
693 |
(rc == GLOB_ABEND) ? "GLOB_ABEND" : |
694 |
"(unknown)"); |
695 |
} |
696 |
} |
697 |
} |
698 |
|
699 |
filter_out_excludes(pattern, path_len, new_globs_index); |
700 |
|
701 |
if (sysfs_mount_point == NULL) { |
702 |
filter_out_non_block_devices(new_globs_index); |
703 |
} |
704 |
|
705 |
LOG_EXIT_VOID(); |
706 |
} |
707 |
|
708 |
static char dir_pattern[PATH_MAX]; |
709 |
|
710 |
static void process_dir(char * name) |
711 |
{ |
712 |
int i; |
713 |
glob_t dirs_glob; |
714 |
|
715 |
LOG_ENTRY(); |
716 |
|
717 |
/* Process entries in this directory. */ |
718 |
get_dev_names(name); |
719 |
|
720 |
/* Get a list of this directory's subdirectories. */ |
721 |
strcpy(dir_pattern, name); |
722 |
strcat(dir_pattern, "*/"); |
723 |
|
724 |
if (glob(dir_pattern, 0, NULL, &dirs_glob) == 0) { |
725 |
|
726 |
/* Process the subdirectories. */ |
727 |
for (i = 0; i < dirs_glob.gl_pathc; i++) { |
728 |
int status; |
729 |
struct stat statbuf; |
730 |
|
731 |
status = stat(dirs_glob.gl_pathv[i], &statbuf); |
732 |
if (status == 0) { |
733 |
if (S_ISDIR(statbuf.st_mode)) { |
734 |
process_dir(dirs_glob.gl_pathv[i]); |
735 |
} |
736 |
} |
737 |
} |
738 |
|
739 |
globfree(&dirs_glob); |
740 |
} |
741 |
|
742 |
LOG_EXIT_VOID(); |
743 |
} |
744 |
|
745 |
static char dir_path[PATH_MAX]; |
746 |
|
747 |
static void get_legacy_devs() |
748 |
{ |
749 |
int base_len, i; |
750 |
char * pch; |
751 |
|
752 |
LOG_ENTRY(); |
753 |
|
754 |
memset(&dev_names_glob, 0, sizeof(dev_names_glob)); |
755 |
glob_flags = 0; |
756 |
|
757 |
/* Make sure the user-specified directory ends with a '/'. */ |
758 |
base_len = strlen(scan); |
759 |
if (scan[base_len-1] != '/') { |
760 |
pch = malloc(base_len + 2); |
761 |
if (pch) { |
762 |
strcpy(pch, scan); |
763 |
strcpy(pch + base_len, "/"); |
764 |
scan = pch; |
765 |
base_len += 2; |
766 |
} |
767 |
} |
768 |
|
769 |
base_directory = strdup(scan); |
770 |
base_directory_len = strlen(base_directory); |
771 |
|
772 |
/* Always find devices in the base directory. */ |
773 |
get_dev_names(base_directory); |
774 |
|
775 |
/* Recursively search any subdirectories the user specified. */ |
776 |
strcpy(dir_path, base_directory); |
777 |
for (i = 0; i < directories_count; i++) { |
778 |
int len; |
779 |
|
780 |
strcpy(dir_path + base_directory_len, directories[i]); |
781 |
|
782 |
len = strlen(dir_path); |
783 |
if (dir_path[len-1] != '/') { |
784 |
strcpy(dir_path + len, "/"); |
785 |
} |
786 |
|
787 |
process_dir(dir_path); |
788 |
} |
789 |
|
790 |
LOG_EXIT_VOID(); |
791 |
} |
792 |
|
793 |
static void get_sysfs_devs() |
794 |
{ |
795 |
LOG_ENTRY(); |
796 |
|
797 |
memset(&dev_names_glob, 0, sizeof(dev_names_glob)); |
798 |
glob_flags = 0; |
799 |
|
800 |
strcpy(dir_path, sysfs_mount_point); |
801 |
strcat(dir_path, "/block/"); |
802 |
|
803 |
base_directory = strdup(dir_path); |
804 |
base_directory_len = strlen(base_directory); |
805 |
|
806 |
LOG_DEBUG("Scanning %s\n", dir_path); |
807 |
get_dev_names(dir_path); |
808 |
|
809 |
LOG_EXIT_VOID(); |
810 |
} |
811 |
|
812 |
/** |
813 |
* get_sysfs_size |
814 |
* @full_name: Full path-name to the disk device-node. |
815 |
* @p_size: Return pointer to the disk's size (in sectors). |
816 |
* |
817 |
* Use sysfs to get the size (in sectors) of the specified disk. |
818 |
**/ |
819 |
static int get_sysfs_size(char * full_name, u_int64_t * p_size) |
820 |
{ |
821 |
int rc = 0; |
822 |
int fd; |
823 |
char * size_file = malloc(strlen(full_name) + 6); |
824 |
char size_str[24]; |
825 |
|
826 |
LOG_ENTRY(); |
827 |
|
828 |
if (size_file != NULL) { |
829 |
strcpy(size_file, full_name); |
830 |
strcat(size_file, "/size"); |
831 |
fd = open(size_file, O_RDONLY); |
832 |
if (fd > 0) { |
833 |
int bytes_read; |
834 |
|
835 |
bytes_read = read(fd, size_str, 24); |
836 |
|
837 |
if (bytes_read > 0) { |
838 |
/* Size is already in sectors. */ |
839 |
*p_size = strtoull(size_str, NULL, 10); |
840 |
|
841 |
} else { |
842 |
if (bytes_read == 0) { |
843 |
LOG_ERROR("No bytes read from %s.\n", size_file); |
844 |
} |
845 |
|
846 |
rc = errno; |
847 |
LOG_ERROR("read() returned error %d: %s\n", rc, strerror(rc)); |
848 |
} |
849 |
|
850 |
close(fd); |
851 |
|
852 |
} else { |
853 |
rc = errno; |
854 |
LOG_ERROR("open(%s) returned error %d: %s\n", size_file, rc, strerror(rc)); |
855 |
} |
856 |
|
857 |
free(size_file); |
858 |
} |
859 |
|
860 |
LOG_EXIT_INT(rc); |
861 |
return rc; |
862 |
} |
863 |
|
864 |
/** |
865 |
* get_legacy_size |
866 |
* @full_name: Full path-name to the disk device-node. |
867 |
* @p_size: Return pointer to the disk's size (in sectors). |
868 |
* |
869 |
* Use the BLKGETSIZE64 ioctl to get the size (in sectors) of the |
870 |
* specified disk. |
871 |
**/ |
872 |
static int get_legacy_size(char * full_name, u_int64_t * p_size) |
873 |
{ |
874 |
int rc = 0; |
875 |
int fd; |
876 |
|
877 |
LOG_ENTRY(); |
878 |
|
879 |
fd = open(full_name, O_RDONLY); |
880 |
|
881 |
if (fd > 0) { |
882 |
|
883 |
/* Ioctl to get size. (returns bytes) */ |
884 |
rc = ioctl(fd, BLKGETSIZE64, p_size); |
885 |
if (rc == 0) { |
886 |
*p_size >>= EVMS_VSECTOR_SIZE_SHIFT; |
887 |
*p_size &= ~1; |
888 |
} else { |
889 |
rc = errno; |
890 |
LOG_DETAILS("ioctl to get the size returned error code " |
891 |
"%d: %s.\n", rc, strerror(rc)); |
892 |
} |
893 |
|
894 |
close(fd); |
895 |
|
896 |
} else { |
897 |
rc = errno; |
898 |
LOG_DETAILS("open(%s) returned error %d: %s\n", |
899 |
full_name, rc, strerror(rc)); |
900 |
} |
901 |
|
902 |
LOG_EXIT_INT(rc); |
903 |
return rc; |
904 |
} |
905 |
|
906 |
/** |
907 |
* get_disk_size |
908 |
* @full_name: Full path-name to the device node. |
909 |
* @disk: Pointer to the disk object. |
910 |
* |
911 |
* Get the size of the disk, and check that it is non-zero. |
912 |
**/ |
913 |
static int get_disk_size(char * full_name, storage_object_t * disk) |
914 |
{ |
915 |
int rc; |
916 |
LOG_ENTRY(); |
917 |
|
918 |
if (sysfs_mount_point) { |
919 |
rc = get_sysfs_size(full_name, &disk->size); |
920 |
} else { |
921 |
rc = get_legacy_size(full_name, &disk->size); |
922 |
} |
923 |
|
924 |
if (disk->size == 0) { |
925 |
LOG_DEBUG("Disk %s has zero-size. Not a valid disk.\n", |
926 |
disk->name); |
927 |
rc = EINVAL; |
928 |
} else if (disk->dev_major == NBD_MAJOR && |
929 |
! is_drbd_active()) { |
930 |
|
931 |
/* YUCK!!! Uninitialized NBD devices report a size anyway. |
932 |
* DRBD (which shares the same major) behaves correctly. |
933 |
*/ |
934 |
if (EngFncs->is_2_4_kernel()) { |
935 |
if (disk->size == NBD_DEF_SIZE_2_4) { |
936 |
LOG_DEBUG("Disk %s appears to be an uninitialized NBD " |
937 |
"device.\n", disk->name); |
938 |
rc = EINVAL; |
939 |
} |
940 |
|
941 |
} else { |
942 |
if (disk->size == NBD_DEF_SIZE_2_6) { |
943 |
LOG_DEBUG("Disk %s appears to be an uninitialized NBD " |
944 |
"device.\n", disk->name); |
945 |
rc = EINVAL; |
946 |
} |
947 |
} |
948 |
} |
949 |
|
950 |
LOG_EXIT_INT(rc); |
951 |
return rc; |
952 |
} |
953 |
|
954 |
/** |
955 |
* get_sysfs_major_minor |
956 |
* @full_name: Full path-name to the disk device-node. |
957 |
* @p_major: Return pointer to the disk's major-number. |
958 |
* @p_minor: Return pointer to the disk's minor-number. |
959 |
* |
960 |
* Use sysfs to get the device-number for the specified disk. |
961 |
**/ |
962 |
static int get_sysfs_major_minor(char * full_name, |
963 |
u_int32_t * p_major, |
964 |
u_int32_t * p_minor) |
965 |
{ |
966 |
int rc = 0; |
967 |
int fd; |
968 |
char * dev_file = malloc(strlen(full_name) + 5); |
969 |
char dev_str[16]; |
970 |
dev_t dev; |
971 |
|
972 |
LOG_ENTRY(); |
973 |
|
974 |
if (dev_file != NULL) { |
975 |
strcpy(dev_file, full_name); |
976 |
strcat(dev_file, "/dev"); |
977 |
fd = open(dev_file, O_RDONLY); |
978 |
if (fd > 0) { |
979 |
int bytes_read; |
980 |
|
981 |
bytes_read = read(fd, dev_str, 16); |
982 |
|
983 |
if (bytes_read > 0) { |
984 |
rc = sscanf(dev_str, "%u:%u", p_major, p_minor); |
985 |
if (rc != 2) { |
986 |
dev = strtoul(dev_str, NULL, 16); |
987 |
*p_major = major(dev); |
988 |
*p_minor = minor(dev); |
989 |
} |
990 |
rc = 0; |
991 |
} else { |
992 |
if (bytes_read == 0) { |
993 |
LOG_ERROR("No bytes read from %s.\n", dev_file); |
994 |
} |
995 |
|
996 |
rc = errno; |
997 |
LOG_ERROR("read() returned error %d: %s\n", rc, strerror(rc)); |
998 |
} |
999 |
|
1000 |
close(fd); |
1001 |
|
1002 |
} else { |
1003 |
rc = errno; |
1004 |
LOG_ERROR("open(%s) returned error %d: %s\n", dev_file, rc, strerror(rc)); |
1005 |
} |
1006 |
} |
1007 |
|
1008 |
LOG_EXIT_INT(rc); |
1009 |
return rc; |
1010 |
} |
1011 |
|
1012 |
/** |
1013 |
* get_legacy_major_minor |
1014 |
* @full_name: Full path-name to the disk device-node. |
1015 |
* @p_major: Return pointer to the disk's major-number. |
1016 |
* @p_minor: Return pointer to the disk's minor-number. |
1017 |
* |
1018 |
* Use stat to get the device-number for the specified disk. |
1019 |
**/ |
1020 |
static int get_legacy_major_minor(char * full_name, |
1021 |
u_int32_t * p_major, |
1022 |
u_int32_t * p_minor) |
1023 |
{ |
1024 |
int rc = 0; |
1025 |
struct stat statbuf; |
1026 |
|
1027 |
LOG_ENTRY(); |
1028 |
|
1029 |
rc = stat(full_name, &statbuf); |
1030 |
if (rc == 0) { |
1031 |
*p_major = major(statbuf.st_rdev); |
1032 |
*p_minor = minor(statbuf.st_rdev); |
1033 |
|
1034 |
} else { |
1035 |
rc = errno; |
1036 |
LOG_ERROR("stat(%s) returned error code %d: %s\n", |
1037 |
full_name, rc, strerror(rc)); |
1038 |
} |
1039 |
|
1040 |
LOG_EXIT_INT(rc); |
1041 |
return rc; |
1042 |
} |
1043 |
|
1044 |
/** |
1045 |
* check_for_duplicate_dev |
1046 |
* |
1047 |
* Search the current output list for the device-number of the specified disk. |
1048 |
* Each device-number should only be discovered once. |
1049 |
**/ |
1050 |
static int check_for_duplicate_dev(storage_object_t * new_disk, |
1051 |
list_anchor_t output_list) |
1052 |
{ |
1053 |
storage_object_t * disk; |
1054 |
list_element_t itr; |
1055 |
|
1056 |
LOG_ENTRY(); |
1057 |
|
1058 |
LIST_FOR_EACH(output_list, itr, disk) { |
1059 |
if (disk->dev_major == new_disk->dev_major && |
1060 |
disk->dev_minor == new_disk->dev_minor) { |
1061 |
LOG_WARNING("Current disk %s has device-number %x:%x, which" |
1062 |
"is a duplicate of disk %s. Ignoring %s.\n", |
1063 |
new_disk->name, new_disk->dev_major, |
1064 |
new_disk->dev_minor, disk->name, new_disk->name); |
1065 |
LOG_EXIT_INT(EINVAL); |
1066 |
return EINVAL; |
1067 |
} |
1068 |
} |
1069 |
|
1070 |
LOG_EXIT_INT(0); |
1071 |
return 0; |
1072 |
} |
1073 |
|
1074 |
/** |
1075 |
* get_disk_devnum |
1076 |
* @full_name: Full path-name to the disk device-node. |
1077 |
* @disk: Pointer to the disk object. |
1078 |
* @output_list:Current list of discovered disks. |
1079 |
* |
1080 |
* Get the device-number for the specified disk. Check that the device-number |
1081 |
* is allowed, and that it isn't a duplicate of an already-discovered disk. |
1082 |
**/ |
1083 |
static int get_disk_devnum(char * full_name, |
1084 |
storage_object_t * disk, |
1085 |
list_anchor_t output_list) |
1086 |
{ |
1087 |
int rc; |
1088 |
LOG_ENTRY(); |
1089 |
|
1090 |
if (sysfs_mount_point) { |
1091 |
rc = get_sysfs_major_minor(full_name, &disk->dev_major, |
1092 |
&disk->dev_minor); |
1093 |
} else { |
1094 |
rc = get_legacy_major_minor(full_name, &disk->dev_major, |
1095 |
&disk->dev_minor); |
1096 |
} |
1097 |
if (rc) { |
1098 |
goto out; |
1099 |
} |
1100 |
|
1101 |
/* Exclude floppy, md, and lvm1 devices. */ |
1102 |
if (disk->dev_major == FLOPPY_MAJOR || |
1103 |
disk->dev_major == MD_MAJOR || |
1104 |
disk->dev_major == LVM_MAJOR) { |
1105 |
LOG_DEBUG("Disk %s has a disallowed major number: %d.\n", |
1106 |
disk->name, disk->dev_major); |
1107 |
rc = EINVAL; |
1108 |
goto out; |
1109 |
} |
1110 |
|
1111 |
/* Only discover a given device-number once. */ |
1112 |
rc = check_for_duplicate_dev(disk, output_list); |
1113 |
|
1114 |
out: |
1115 |
LOG_EXIT_INT(rc); |
1116 |
return rc; |
1117 |
} |
1118 |
|
1119 |
/** |
1120 |
* get_dm_device_list |
1121 |
* |
1122 |
* Get the list of current DM devices (if we haven't gotten it previously). |
1123 |
**/ |
1124 |
static dm_device_list_t * get_dm_device_list(void) |
1125 |
{ |
1126 |
int rc; |
1127 |
|
1128 |
LOG_ENTRY(); |
1129 |
|
1130 |
if (!dm_devices) { |
1131 |
rc = EngFncs->dm_get_devices(&dm_devices); |
1132 |
if (rc) { |
1133 |
LOG_ERROR("Error calling dm_get_devices.\n"); |
1134 |
} |
1135 |
} |
1136 |
|
1137 |
LOG_EXIT_PTR(dm_devices); |
1138 |
return dm_devices; |
1139 |
} |
1140 |
|
1141 |
/** |
1142 |
* find_disk_in_dm_devices |
1143 |
* |
1144 |
* Search the DM devices list for an entry with the same major:minor |
1145 |
* as this disk. |
1146 |
**/ |
1147 |
static dm_device_list_t * find_disk_in_dm_devices(storage_object_t * disk, |
1148 |
dm_device_list_t * dm_list) |
1149 |
{ |
1150 |
dm_device_list_t * dm_entry; |
1151 |
|
1152 |
LOG_ENTRY(); |
1153 |
|
1154 |
for (dm_entry = dm_list; dm_entry; dm_entry = dm_entry->next) { |
1155 |
if (dm_entry->dev_major == disk->dev_major && |
1156 |
dm_entry->dev_minor == disk->dev_minor) { |
1157 |
goto out; |
1158 |
} |
1159 |
} |
1160 |
|
1161 |
out: |
1162 |
LOG_EXIT_PTR(dm_entry); |
1163 |
return dm_entry; |
1164 |
} |
1165 |
|
1166 |
/** |
1167 |
* check_multipath_name |
1168 |
* |
1169 |
* Other EVMS plugins can create multipath devices. We *don't* want to |
1170 |
* recognize those devices as disks. So check the name that we got from |
1171 |
* DM to see if it uses the naming format of the EVMS multipath plugins. |
1172 |
**/ |
1173 |
static int check_multipath_name(storage_object_t *disk) |
1174 |
{ |
1175 |
int rc; |
1176 |
|
1177 |
LOG_ENTRY(); |
1178 |
|
1179 |
/* Multipath-segment-manager devices start with "mp/". */ |
1180 |
rc = strncmp(disk->name, "mp/", 3); |
1181 |
if (rc) { |
1182 |
/* MD-multipath devices start with "md/". */ |
1183 |
rc = strncmp(disk->name, "md/", 3); |
1184 |
} |
1185 |
|
1186 |
rc = rc ? 0 : EINVAL; |
1187 |
|
1188 |
LOG_EXIT_INT(rc); |
1189 |
return rc; |
1190 |
} |
1191 |
|
1192 |
/** |
1193 |
* update_multipath_child_list |
1194 |
* |
1195 |
* Search the multipath target info for all the child devices. Add these |
1196 |
* devices to the global list so we can filter these out at the end of |
1197 |
* discovery. That way we won't discover both the multipath devices and |
1198 |
* their component disks, which would lead to duplicate discoveries in the |
1199 |
* higher levels. |
1200 |
* |
1201 |
* Use a temporary list to build up the list of children for this multipath. |
1202 |
* Then append this list to the global one once we've collected all the |
1203 |
* children. This will prevent hitting an error part way through processing |
1204 |
* the list of child devices, which could leave the global list in an |
1205 |
* inconsistent state. |
1206 |
**/ |
1207 |
static int update_multipath_child_list(dm_target_t * targets) |
1208 |
{ |
1209 |
dm_target_multipath_t * mp = targets->data.multipath; |
1210 |
dm_priority_group_t * pg; |
1211 |
dm_path_t * path; |
1212 |
dm_device_t * device; |
1213 |
list_anchor_t children = NULL; |
1214 |
list_element_t itr1, itr2; |
1215 |
int i, j, rc = 0; |
1216 |
|
1217 |
LOG_ENTRY(); |
1218 |
|
1219 |
/* Allocate the global child list if it doesn't exist yet. */ |
1220 |
if (!multipath_children) { |
1221 |
multipath_children = EngFncs->allocate_list(); |
1222 |
if (!multipath_children) { |
1223 |
LOG_ERROR("Error allocating multipath_children list.\n"); |
1224 |
rc = ENOMEM; |
1225 |
goto out; |
1226 |
} |
1227 |
} |
1228 |
|
1229 |
/* Allocate a temporary list. */ |
1230 |
children = EngFncs->allocate_list(); |
1231 |
if (!children) { |
1232 |
LOG_ERROR("Error allocating temporary child list.\n"); |
1233 |
rc = ENOMEM; |
1234 |
goto out; |
1235 |
} |
1236 |
|
1237 |
/* For each priority group in the multipath. */ |
1238 |
for (i = 0; i < mp->num_groups; i++) { |
1239 |
pg = mp->group + i; |
1240 |
/* For each path in the priority group. */ |
1241 |
for (j = 0; j < pg->num_paths; j++) { |
1242 |
path = pg->path + j; |
1243 |
device = EngFncs->engine_alloc(sizeof(*device)); |
1244 |
if (!device) { |
1245 |
LOG_ERROR("Error allocating device structure " |
1246 |
"for path %d:%d.\n", |
1247 |
path->device.major, path->device.minor); |
1248 |
rc = ENOMEM; |
1249 |
goto out; |
1250 |
} |
1251 |
device->major = path->device.major; |
1252 |
device->minor = path->device.minor; |
1253 |
|
1254 |
/* Add this path's device to the temporary list. */ |
1255 |
itr1 = EngFncs->insert_thing(children, device, |
1256 |
INSERT_AFTER, NULL); |
1257 |
if (!itr1) { |
1258 |
LOG_ERROR("Error adding device %d:%d to the " |
1259 |
"temporary child list.\n", |
1260 |
device->major, device->minor); |
1261 |
rc = ENOMEM; |
1262 |
goto out; |
1263 |
} |
1264 |
} |
1265 |
} |
1266 |
|
1267 |
out: |
1268 |
if (!rc) { |
1269 |
/* Success. Append the temporary list to the global list. */ |
1270 |
rc = EngFncs->merge_lists(multipath_children, children, NULL, NULL); |
1271 |
if (rc) { |
1272 |
LOG_ERROR("Error merging temporary list with " |
1273 |
"multipath_children list.\n"); |
1274 |
} |
1275 |
} |
1276 |
if (rc) { |
1277 |
/* Some error occurred. Delete the temporary |
1278 |
* list and all of it's devices. |
1279 |
*/ |
1280 |
if (children) { |
1281 |
LIST_FOR_EACH_SAFE(children, itr1, itr2, device) { |
1282 |
EngFncs->delete_element(itr1); |
1283 |
EngFncs->engine_free(device); |
1284 |
} |
1285 |
} |
1286 |
} |
1287 |
if (children) { |
1288 |
EngFncs->destroy_list(children); |
1289 |
} |
1290 |
|
1291 |
LOG_EXIT_INT(rc); |
1292 |
return rc; |
1293 |
} |
1294 |
|
1295 |
/** |
1296 |
* check_multipath |
1297 |
* |
1298 |
* Check if this disk is a DM multipath device. |
1299 |
**/ |
1300 |
static int check_multipath(storage_object_t * disk) |
1301 |
{ |
1302 |
dm_device_list_t * dm_list, * dm_entry; |
1303 |
dm_target_t * targets = NULL; |
1304 |
local_disk_t * ld = disk->private_data; |
1305 |
int rc = 0; |
1306 |
|
1307 |
LOG_ENTRY(); |
1308 |
|
1309 |
/* Get the list of active DM devices. */ |
1310 |
dm_list = get_dm_device_list(); |
1311 |
if (!dm_list) { |
1312 |
LOG_WARNING("Cannot get list of DM devices.\n"); |
1313 |
goto out; |
1314 |
} |
1315 |
|
1316 |
/* Search the DM list for an entry that matches this disk. */ |
1317 |
dm_entry = find_disk_in_dm_devices(disk, dm_list); |
1318 |
if (!dm_entry) { |
1319 |
LOG_DEBUG("Disk %s is not a DM device.\n", disk->name); |
1320 |
goto out; |
1321 |
} |
1322 |
|
1323 |
/* Copy the DM name to this disk. */ |
1324 |
LOG_DEBUG("Changing disk name from %s to %s.\n", |
1325 |
disk->name, dm_entry->name); |
1326 |
strncpy(disk->name, dm_entry->name, EVMS_NAME_SIZE); |
1327 |
|
1328 |
/* Get the DM mapping for this disk. */ |
1329 |
rc = EngFncs->dm_get_targets(disk, &targets); |
1330 |
if (rc) { |
1331 |
LOG_ERROR("Error getting DM mapping for disk %s.\n", disk->name); |
1332 |
goto out; |
1333 |
} |
1334 |
|
1335 |
/* Reject all non-multipath devices. */ |
1336 |
if (targets->type != DM_TARGET_MULTIPATH) { |
1337 |
LOG_DEBUG("Disk %s is not a multipath device.\n", disk->name); |
1338 |
rc = EINVAL; |
1339 |
goto out; |
1340 |
} |
1341 |
|
1342 |
/* Reject all multipath devices that |
1343 |
* were created by other EVMS plugins. |
1344 |
*/ |
1345 |
rc = check_multipath_name(disk); |
1346 |
if (rc) { |
1347 |
LOG_DEBUG("Multipath disk %s belongs to another EVMS plugin.\n", |
1348 |
disk->name); |
1349 |
goto out; |
1350 |
} |
1351 |
|
1352 |
rc = update_multipath_child_list(targets); |
1353 |
if (rc) { |
1354 |
LOG_DEBUG("Error building list of children of " |
1355 |
"multipath disk %s.\n", disk->name); |
1356 |
goto out; |
1357 |
} |
1358 |
|
1359 |
ld->flags |= LD_FLAG_MULTIPATH; |
1360 |
|
1361 |
out: |
1362 |
EngFncs->dm_deallocate_targets(targets); |
1363 |
LOG_EXIT_INT(rc); |
1364 |
return rc; |
1365 |
} |
1366 |
|
1367 |
/** |
1368 |
* remove_multipath_children |
1369 |
* |
1370 |
* Compare the multipath_children list with the discovery output list. Any |
1371 |
* disks on the multipath_children list must be removed from the output list. |
1372 |
**/ |
1373 |
static void remove_multipath_children(list_anchor_t multipath_children, |
1374 |
list_anchor_t output_list) |
1375 |
{ |
1376 |
list_element_t itr1, itr2, itr3; |
1377 |
storage_object_t * disk; |
1378 |
dm_device_t * child; |
1379 |
|
1380 |
LOG_ENTRY(); |
1381 |
|
1382 |
LIST_FOR_EACH(multipath_children, itr3, child) { |
1383 |
LIST_FOR_EACH_SAFE(output_list, itr1, itr2, disk) { |
1384 |
if (child->major == disk->dev_major && |
1385 |
child->minor == disk->dev_minor) { |
1386 |
EngFncs->delete_element(itr1); |
1387 |
close_dev(disk); |
1388 |
EngFncs->engine_free(disk->private_data); |
1389 |
disk->flags &= ~SOFLAG_ACTIVE; |
1390 |
EngFncs->free_logical_disk(disk); |
1391 |
} |
1392 |
} |
1393 |
} |
1394 |
|
1395 |
LOG_EXIT_VOID(); |
1396 |
} |
1397 |
|
1398 |
/** |
1399 |
* get_geometry |
1400 |
* |
1401 |
* Use the HDIO_GETGEO_BIG or HDIO_GETGEO ioctl to get the disk's geometry. |
1402 |
* Check that the geometry is valid for a disk. |
1403 |
**/ |
1404 |
static int get_geometry(storage_object_t * disk) |
1405 |
{ |
1406 |
struct hd_big_geometry big_geometry; |
1407 |
struct hd_geometry geometry; |
1408 |
local_disk_t * ld = disk->private_data; |
1409 |
int rc; |
1410 |
|
1411 |
LOG_ENTRY(); |
1412 |
|
1413 |
rc = ioctl(ld->fd, HDIO_GETGEO, &geometry); |
1414 |
if (rc) { |
1415 |
rc = ioctl(ld->fd, HDIO_GETGEO_BIG, &big_geometry); |
1416 |
if (rc) { |
1417 |
rc = errno; |
1418 |
LOG_DEBUG("Error getting geometry for disk %s: %d: " |
1419 |
"%s.\n", disk->name, rc, strerror(rc)); |
1420 |
} else if (big_geometry.start != 0) { |
1421 |
/* A disk's geometry must start at offset 0. */ |
1422 |
LOG_DEBUG("Geometry for disk %s reports a non-zero starting " |
1423 |
"offset. Not a valid disk.\n", disk->name); |
1424 |
rc = EINVAL; |
1425 |
} else { |
1426 |
disk->geometry.cylinders = big_geometry.cylinders; |
1427 |
disk->geometry.heads = big_geometry.heads; |
1428 |
disk->geometry.sectors_per_track = big_geometry.sectors; |
1429 |
} |
1430 |
} else { |
1431 |
if (geometry.start != 0) { |
1432 |
/* A disk's geometry must start at offset 0. */ |
1433 |
LOG_DEBUG("Geometry for disk %s reports a non-zero starting " |
1434 |
"offset. Not a valid disk.\n", disk->name); |
1435 |
rc = EINVAL; |
1436 |
} else { |
1437 |
/* ala fdisk: never use geometry.cylinders - it is truncated */ |
1438 |
// disk->geometry.cylinders = geometry.cylinders; |
1439 |
disk->geometry.heads = geometry.heads; |
1440 |
disk->geometry.sectors_per_track = geometry.sectors; |
1441 |
} |
1442 |
} |
1443 |
|
1444 |
/* |
1445 |
* If the driver answered the ioctl but didn't supply a geometry, |
1446 |
* fill in a default geometry. This is what fdisk does. |
1447 |
*/ |
1448 |
if (rc == 0) { |
1449 |
if (disk->geometry.heads == 0) { |
1450 |
disk->geometry.heads = 255; |
1451 |
} |
1452 |
if (disk->geometry.sectors_per_track == 0) { |
1453 |
disk->geometry.sectors_per_track = 63; |
1454 |
} |
1455 |
if (disk->geometry.cylinders == 0) { |
1456 |
disk->geometry.cylinders = disk->size / (255 * 63 * |
1457 |
(disk->geometry.bytes_per_sector / 512)); |
1458 |
} |
1459 |
} |
1460 |
|
1461 |
LOG_EXIT_INT(rc); |
1462 |
return rc; |
1463 |
} |
1464 |
|
1465 |
/** |
1466 |
* get_fake_geometry |
1467 |
* |
1468 |
* Some drivers (notably loop, nbd, and drbd) don't |
1469 |
* support geometry, so we need to fake it. |
1470 |
**/ |
1471 |
static int get_fake_geometry(storage_object_t * disk) |
1472 |
{ |
1473 |
local_disk_t * ld = disk->private_data; |
1474 |
int rc = EINVAL; |
1475 |
|
1476 |
LOG_ENTRY(); |
1477 |
|
1478 |
if (disk->dev_major == LOOP_MAJOR || |
1479 |
disk->dev_major == NBD_MAJOR || |
1480 |
disk->dev_major == DRBD_MAJOR || |
1481 |
ld->flags & LD_FLAG_MULTIPATH) { |
1482 |
LOG_DEBUG("Creating fake geometry for disk %s.\n", disk->name); |
1483 |
disk->geometry.heads = 255; |
1484 |
disk->geometry.sectors_per_track = 63; |
1485 |
disk->geometry.cylinders = disk->size / (255 * 63); |
1486 |
rc = 0; |
1487 |
} |
1488 |
|
1489 |
LOG_EXIT_INT(rc); |
1490 |
return rc; |
1491 |
} |
1492 |
|
1493 |
/** |
1494 |
* get_block_size |
1495 |
* |
1496 |
* Use the BLKBSZGET ioctl to get the block-size for the specified disk. |
1497 |
**/ |
1498 |
static int get_block_size(storage_object_t * disk) |
1499 |
{ |
1500 |
local_disk_t * ld = disk->private_data; |
1501 |
int rc, block_size; |
1502 |
|
1503 |
LOG_ENTRY(); |
1504 |
|
1505 |
rc = ioctl(ld->fd, BLKBSZGET, &block_size); |
1506 |
if (rc) { |
1507 |
rc = errno; |
1508 |
LOG_ERROR("Error getting block size for disk %s: %d: %s.\n", |
1509 |
disk->name, rc, strerror(rc)); |
1510 |
} else { |
1511 |
LOG_DEBUG("Disk %s has block-size %d.\n", |
1512 |
disk->name, block_size); |
1513 |
disk->geometry.block_size = block_size; |
1514 |
} |
1515 |
|
1516 |
LOG_EXIT_INT(rc); |
1517 |
return rc; |
1518 |
} |
1519 |
|
1520 |
/** |
1521 |
* set_block_size |
1522 |
* |
1523 |
* Use the BLKBSZSET ioctl to set the disk's block-size. |
1524 |
**/ |
1525 |
static int set_block_size(storage_object_t * disk, int block_size) |
1526 |
{ |
1527 |
local_disk_t * ld = disk->private_data; |
1528 |
int rc; |
1529 |
|
1530 |
LOG_ENTRY(); |
1531 |
|
1532 |
rc = ioctl(ld->fd, BLKBSZSET, &block_size); |
1533 |
if (rc) { |
1534 |
rc = errno; |
1535 |
LOG_ERROR("Error setting block size (%d) for disk %s: %d: " |
1536 |
"%s.\n", block_size, disk->name, rc, strerror(rc)); |
1537 |
} else { |
1538 |
LOG_DEBUG("Setting disk %s block-size to %d.\n", |
1539 |
disk->name, block_size); |
1540 |
disk->geometry.block_size = block_size; |
1541 |
} |
1542 |
|
1543 |
LOG_EXIT_INT(rc); |
1544 |
return rc; |
1545 |
} |
1546 |
|
1547 |
/** |
1548 |
* get_hardsector_size |
1549 |
* |
1550 |
* Use the BLKSSZGET ioctl to get the disk's hard-sector-size. If the ioctl |
1551 |
* returns an error (some drivers don't support it yet), fall back on the |
1552 |
* default sector-size (which is the same default used in the kernel - see |
1553 |
* include/linux/blkdev.h::get_hardsect_size). |
1554 |
**/ |
1555 |
static void get_hardsector_size(storage_object_t * disk) |
1556 |
{ |
1557 |
u_int32_t hardsector_size; |
1558 |
local_disk_t * ld = disk->private_data; |
1559 |
int rc; |
1560 |
|
1561 |
LOG_ENTRY(); |
1562 |
|
1563 |
rc = ioctl(ld->fd, BLKSSZGET, &hardsector_size); |
1564 |
if (rc) { |
1565 |
rc = errno; |
1566 |
LOG_DEBUG("Error getting hardsector size for disk %s: %d: " |
1567 |
"%s.\n", disk->name, rc, strerror(rc)); |
1568 |
hardsector_size = EVMS_VSECTOR_SIZE; |
1569 |
} |
1570 |
disk->geometry.bytes_per_sector = hardsector_size; |
1571 |
|
1572 |
LOG_EXIT_VOID(); |
1573 |
} |
1574 |
|
1575 |
/** |
1576 |
* find_disk_type |
1577 |
* |
1578 |
* Determine if this disk is IDE, SCSI, or something else. This is done by |
1579 |
* examining the name of the disk. Names starting with "hd" or that contain |
1580 |
* "ide" will be marked as IDE. Names starting with "sd" or that contain |
1581 |
* "scsi" will be marked as SCSI. |
1582 |
**/ |
1583 |
static void find_disk_type(storage_object_t * disk) |
1584 |
{ |
1585 |
local_disk_t * ld = disk->private_data; |
1586 |
|
1587 |
LOG_ENTRY(); |
1588 |
|
1589 |
if (!strncmp(disk->name, "hd", 2) || |
1590 |
strstr(disk->name, "ide")) { |
1591 |
ld->flags |= LD_FLAG_IDE; |
1592 |
} else if (!strncmp(disk->name, "sd", 2) || |
1593 |
strstr(disk->name, "scsi")) { |
1594 |
ld->flags |= LD_FLAG_SCSI; |
1595 |
} |
1596 |
|
1597 |
LOG_DEBUG("Type of disk %s is %s\n", disk->name, |
1598 |
(ld->flags & LD_FLAG_IDE) ? "IDE" : |
1599 |
(ld->flags & LD_FLAG_SCSI) ? "SCSI" : "Unknown"); |
1600 |
|
1601 |
LOG_EXIT_VOID(); |
1602 |
} |
1603 |
|
1604 |
/** |
1605 |
* create_logical_disk |
1606 |
* |
1607 |
* Allocate a new disk and initialize all fields. |
1608 |
**/ |
1609 |
static storage_object_t * create_logical_disk(storage_object_t * working_disk) |
1610 |
{ |
1611 |
storage_object_t * disk = NULL; |
1612 |
local_disk_t * working_ld = working_disk->private_data; |
1613 |
local_disk_t * ld; |
1614 |
char *name; |
1615 |
int rc; |
1616 |
|
1617 |
LOG_ENTRY(); |
1618 |
|
1619 |
/* Replace exclaimation marks with slashes in the disk name. */ |
1620 |
for (name = working_disk->name; *name; name++) { |
1621 |
if (*name == '!') *name = '/'; |
1622 |
} |
1623 |
|
1624 |
rc = EngFncs->allocate_logical_disk(working_disk->name, &disk); |
1625 |
if (rc) { |
1626 |
LOG_SERIOUS("Error allocating new disk object for disk %s: %d: " |
1627 |
"%s.\n", working_disk->name, rc, EngFncs->strerror(rc)); |
1628 |
goto out; |
1629 |
} |
1630 |
|
1631 |
disk->private_data = EngFncs->engine_alloc(sizeof(local_disk_t)); |
1632 |
if (!disk->private_data) { |
1633 |
LOG_SERIOUS("Error allocating private data for disk %s.\n", |
1634 |
disk->name); |
1635 |
EngFncs->free_logical_disk(disk); |
1636 |
disk = NULL; |
1637 |
goto out; |
1638 |
} |
1639 |
ld = disk->private_data; |
1640 |
|
1641 |
/* Initialize the logical disk structure */ |
1642 |
disk->data_type = DATA_TYPE; |
1643 |
disk->dev_major = working_disk->dev_major; |
1644 |
disk->dev_minor = working_disk->dev_minor; |
1645 |
disk->plugin = my_plugin_record; |
1646 |
disk->flags = SOFLAG_ACTIVE; |
1647 |
disk->size = working_disk->size; |
1648 |
disk->geometry.cylinders = working_disk->geometry.cylinders; |
1649 |
disk->geometry.heads = working_disk->geometry.heads; |
1650 |
disk->geometry.sectors_per_track= working_disk->geometry.sectors_per_track; |
1651 |
disk->geometry.bytes_per_sector = working_disk->geometry.bytes_per_sector; |
1652 |
disk->geometry.block_size = working_disk->geometry.block_size; |
1653 |
|
1654 |
/* Fill in the boot cylinder limit (LBA of 1st sector above boot |
1655 |
* cylinder) for this drive. If the drive is too small then the limit |
1656 |
* is the size of the drive. Otherwise the limit is calculated. |
1657 |
*/ |
1658 |
disk->geometry.boot_cylinder_limit = (disk->geometry.cylinders < 1024) ? |
1659 |
disk->size : |
1660 |
(disk->geometry.heads * disk->geometry.sectors_per_track * 1023); |
1661 |
|
1662 |
*ld = *working_ld; |
1663 |
ld->file_handle->disk = disk; |
1664 |
|
1665 |
find_disk_type(disk); |
1666 |
|
1667 |
LOG_DETAILS("New Logical Disk:\n"); |
1668 |
LOG_DETAILS(" name: %s\n", disk->name); |
1669 |
LOG_DETAILS(" size: %"PRIu64"\n", disk->size); |
1670 |
LOG_DETAILS(" device-number: %x:%x\n", disk->dev_major, disk->dev_minor); |
1671 |
LOG_DETAILS(" file-descriptor: %d\n", ld->fd); |
1672 |
LOG_DETAILS(" geometry:\n"); |
1673 |
LOG_DETAILS(" cylinders: %"PRIu64"\n", disk->geometry.cylinders); |
1674 |
LOG_DETAILS(" heads: %d\n", disk->geometry.heads); |
1675 |
LOG_DETAILS(" sectors: %d\n", disk->geometry.sectors_per_track); |
1676 |
LOG_DETAILS(" sector size: %d (bytes)\n", disk->geometry.bytes_per_sector); |
1677 |
LOG_DETAILS(" block size: %"PRIu64" (bytes)\n", disk->geometry.block_size); |
1678 |
|
1679 |
out: |
1680 |
LOG_EXIT_PTR(disk); |
1681 |
return disk; |
1682 |
} |
1683 |
|
1684 |
/** |
1685 |
* LD_discover |
1686 |
**/ |
1687 |
static int LD_discover(list_anchor_t input_list, |
1688 |
list_anchor_t output_list, |
1689 |
boolean final_call) |
1690 |
{ |
1691 |
storage_object_t working_disk; |
1692 |
storage_object_t * disk; |
1693 |
local_disk_t working_ld; |
1694 |
list_element_t itr; |
1695 |
char * full_node_path; |
1696 |
uint count; |
1697 |
int rc, i; |
1698 |
|
1699 |
LOG_ENTRY(); |
1700 |
|
1701 |
/* Get the list of devices to examine. */ |
1702 |
if (sysfs_mount_point) { |
1703 |
get_sysfs_devs(); |
1704 |
} else { |
1705 |
get_legacy_devs(); |
1706 |
} |
1707 |
|
1708 |
for (i = 0; i < dev_names_glob.gl_pathc; i++) { |
1709 |
full_node_path = dev_names_glob.gl_pathv[i]; |
1710 |
LOG_DEBUG("Examining disk %s\n", full_node_path); |
1711 |
|
1712 |
/* Initialize the working disk object. */ |
1713 |
memset(&working_disk, 0, sizeof(working_disk)); |
1714 |
memset(&working_ld, 0, sizeof(working_ld)); |
1715 |
working_disk.private_data = &working_ld; |
1716 |
working_ld.fd = -1; |
1717 |
strncpy(working_disk.name, full_node_path + base_directory_len, |
1718 |
EVMS_NAME_SIZE); |
1719 |
|
1720 |
/* Get the device-number of the disk. */ |
1721 |
rc = get_disk_devnum(full_node_path, &working_disk, output_list); |
1722 |
if (rc) { |
1723 |
continue; |
1724 |
} |
1725 |
|
1726 |
/* Get the size of the disk. */ |
1727 |
rc = get_disk_size(full_node_path, &working_disk); |
1728 |
if (rc) { |
1729 |
continue; |
1730 |
} |
1731 |
|
1732 |
/* Open the disk. */ |
1733 |
rc = open_dev(&working_disk); |
1734 |
if (rc) { |
1735 |
continue; |
1736 |
} |
1737 |
|
1738 |
/* Check for DM-multipath devices. */ |
1739 |
rc = check_multipath(&working_disk); |
1740 |
if (rc) { |
1741 |
close_dev(&working_disk); |
1742 |
continue; |
1743 |
} |
1744 |
|
1745 |
/* Get the disk's hard-sector-size. */ |
1746 |
get_hardsector_size(&working_disk); |
1747 |
|
1748 |
/* Get the disk's geometry. */ |
1749 |
rc = get_geometry(&working_disk); |
1750 |
if (rc) { |
1751 |
rc = get_fake_geometry(&working_disk); |
1752 |
if (rc) { |
1753 |
close_dev(&working_disk); |
1754 |
continue; |
1755 |
} |
1756 |
} |
1757 |
|
1758 |
/* Get the disk's block-size. */ |
1759 |
rc = get_block_size(&working_disk); |
1760 |
if (rc) { |
1761 |
close_dev(&working_disk); |
1762 |
continue; |
1763 |
} |
1764 |
|
1765 |
/* Passed all checks. Create a new disk. */ |
1766 |
disk = create_logical_disk(&working_disk); |
1767 |
if (!disk) { |
1768 |
close_dev(&working_disk); |
1769 |
continue; |
1770 |
} |
1771 |
|
1772 |
/* Insert the new disk into ouput list. */ |
1773 |
itr = EngFncs->insert_thing(output_list, disk, |
1774 |
INSERT_AFTER, NULL); |
1775 |
if (!itr) { |
1776 |
LOG_SERIOUS("Error adding new disk %s to output list. " |
1777 |
"Deleting the disk.\n", disk->name); |
1778 |
EngFncs->engine_free(disk->private_data); |
1779 |
EngFncs->free_logical_disk(disk); |
1780 |
close_dev(&working_disk); |
1781 |
continue; |
1782 |
} |
1783 |
} |
1784 |
|
1785 |
remove_multipath_children(multipath_children, output_list); |
1786 |
|
1787 |
EngFncs->dm_deallocate_device_list(dm_devices); |
1788 |
EngFncs->destroy_list(multipath_children); |
1789 |
|
1790 |
count = EngFncs->list_count(output_list); |
1791 |
LOG_DEBUG("Discovered %d disks.\n", count); |
1792 |
LOG_EXIT_INT(0); |
1793 |
return 0; |
1794 |
} |
1795 |
|
1796 |
/** |
1797 |
* get_alignment_size |
1798 |
* |
1799 |
* Return the size (in bytes) of the alignment restrictions for O_DIRECT. On |
1800 |
* 2.5 kernels, this will be the disk's hard-sector-size. On 2.4 kernels, this |
1801 |
* will be the disk's block-size. Since block-size can change at run-time, |
1802 |
* always check the current block-size. Also, since we want access to as much |
1803 |
* of the disk as possible, try to set the block-size to 1k if it isn't |
1804 |
* already. |
1805 |
**/ |
1806 |
static int get_alignment_size(storage_object_t * disk) |
1807 |
{ |
1808 |
int size; |
1809 |
int min_block_size = max(disk->geometry.bytes_per_sector, 1024); |
1810 |
|
1811 |
LOG_ENTRY(); |
1812 |
|
1813 |
if (EngFncs->is_2_4_kernel()) { |
1814 |
get_block_size(disk); |
1815 |
size = disk->geometry.block_size; |
1816 |
if (size > min_block_size) { |
1817 |
set_block_size(disk, min_block_size); |
1818 |
size = disk->geometry.block_size; |
1819 |
} |
1820 |
} else { |
1821 |
size = disk->geometry.bytes_per_sector; |
1822 |
} |
1823 |
|
1824 |
LOG_EXIT_INT(size); |
1825 |
return size; |
1826 |
} |
1827 |
|
1828 |
/** |
1829 |
* check_alignment |
1830 |
* @align_size: |
1831 |
* @offset: |
1832 |
* @count: |
1833 |
* @buffer: |
1834 |
* |
1835 |
* Check whether the specified offset, count, and buffer are valid for the |
1836 |
* specified alignment restriction. |
1837 |
**/ |
1838 |
static int check_alignment(int align_size, |
1839 |
lsn_t offset, |
1840 |
sector_count_t count, |
1841 |
void * buffer) |
1842 |
{ |
1843 |
int align_sectors = align_size >> EVMS_VSECTOR_SIZE_SHIFT; |
1844 |
int align_mask = align_size - 1; |
1845 |
int rc = 0; |
1846 |
|
1847 |
LOG_ENTRY(); |
1848 |
LOG_EXTRA("Checking alignment.\n"); |
1849 |
LOG_EXTRA("\tAlignment Size: %d bytes\n", align_size); |
1850 |
LOG_EXTRA("\tBuffer: 0x%p\n", buffer); |
1851 |
LOG_EXTRA("\tSector Offset: %"PRIu64"\n", offset); |
1852 |
LOG_EXTRA("\tSector Count: %"PRIu64"\n", count); |
1853 |
|
1854 |
if ((unsigned long)buffer & align_mask) { |
1855 |
rc = EINVAL; |
1856 |
} else if (offset % align_sectors) { |
1857 |
rc = EINVAL; |
1858 |
} else if (count % align_sectors) { |
1859 |
rc = EINVAL; |
1860 |
} |
1861 |
|
1862 |
LOG_EXIT_INT(rc); |
1863 |
return rc; |
1864 |
} |
1865 |
|
1866 |
/** |
1867 |
* get_aligned_buffer |
1868 |
* @offset: Starting offset (in sectors) of engine I/O request. |
1869 |
* @count: Size (in sectors) of engine I/O request. |
1870 |
* @align_size: Size (in bytes) that the I/O must be aligned on. |
1871 |
* @local_offset: Aligned starting offset (in sectors). |
1872 |
* @local_count: Aligned I/O size (in sectors). |
1873 |
* @buffer: Aligned data buffer. |
1874 |
* |
1875 |
* To use O_DIRECT, the buffer passed to read() or write() must be aligned on |
1876 |
* the device's block/sector size. The size and starting offset of the I/O must |
1877 |
* also be a multiple of the block/sector size. |
1878 |
**/ |
1879 |
static int get_aligned_buffer(lsn_t offset, |
1880 |
sector_count_t count, |
1881 |
int align_size, |
1882 |
lsn_t * local_offset, |
1883 |
sector_count_t * local_count, |
1884 |
void ** buffer) |
1885 |
{ |
1886 |
u_int32_t offset_diff; |
1887 |
int rc = 0; |
1888 |
|
1889 |
LOG_ENTRY(); |
1890 |
|
1891 |
/* Round down starting offset to the alignment size. */ |
1892 |
*local_offset = round_down(offset, align_size); |
1893 |
|
1894 |
/* Difference between real offset and local offset. */ |
1895 |
offset_diff = offset - *local_offset; |
1896 |
|
1897 |
/* Round up total count of sectors to alignment size. */ |
1898 |
*local_count = round_up(count + offset_diff, align_size); |
1899 |
|
1900 |
/* Allocate the buffer that will actually perform the I/O. The |
1901 |
* memalign call guarantees that the allocated buffer is |
1902 |
* aligned on the desired alignment-size. |
1903 |
*/ |
1904 |
*buffer = memalign(align_size, *local_count << EVMS_VSECTOR_SIZE_SHIFT); |
1905 |
if (!*buffer) { |
1906 |
rc = ENOMEM; |
1907 |
} |
1908 |
|
1909 |
LOG_EXIT_INT(rc); |
1910 |
return rc; |
1911 |
} |
1912 |
|
1913 |
/** |
1914 |
* LD_read |
1915 |
**/ |
1916 |
static int LD_read(storage_object_t * disk, |
1917 |
lsn_t offset, |
1918 |
sector_count_t count, |
1919 |
void * buffer) |
1920 |
{ |
1921 |
void * local_buffer = NULL; |
1922 |
lsn_t local_offset; |
1923 |
sector_count_t local_count; |
1924 |
local_disk_t * ld = disk->private_data; |
1925 |
int rc, align_size, aligned = FALSE; |
1926 |
|
1927 |
LOG_ENTRY(); |
1928 |
LOG_DEBUG("Read disk:%s offset:%"PRIu64" count:%"PRIu64"\n", |
1929 |
disk->name, offset, count); |
1930 |
|
1931 |
if (offset + count > disk->size) { |
1932 |
LOG_ERROR("Read request past end of disk.\n"); |
1933 |
rc = EINVAL; |
1934 |
goto out; |
1935 |
} |
1936 |
|
1937 |
rc = read_from_cache(disk, offset, count, buffer); |
1938 |
if (!rc) { |
1939 |
/* Found in the cache. */ |
1940 |
goto out; |
1941 |
} |
1942 |
|
1943 |
/* Make sure the disk is open. */ |
1944 |
rc = open_dev(disk); |
1945 |
if (rc) { |
1946 |
goto out; |
1947 |
} |
1948 |
|
1949 |
/* Get the alignment restriction for O_DIRECT. */ |
1950 |
align_size = get_alignment_size(disk); |
1951 |
|
1952 |
/* Check if the supplied buffer, offset, and count |
1953 |
* are valid for the alignment restrictions. |
1954 |
*/ |
1955 |
rc = check_alignment(align_size, offset, count, buffer); |
1956 |
if (rc) { |
1957 |
/* Get a data buffer aligned with this restriction. */ |
1958 |
rc = get_aligned_buffer(offset, count, align_size, &local_offset, |
1959 |
&local_count, &local_buffer); |
1960 |
if (rc) { |
1961 |
goto out; |
1962 |
} |
1963 |
} else { |
1964 |
aligned = TRUE; |
1965 |
local_offset = offset; |
1966 |
local_count = count; |
1967 |
local_buffer = buffer; |
1968 |
} |
1969 |
|
1970 |
/* Send the read to the engine. */ |
1971 |
rc = EngFncs->read_object(disk, ld->fd, local_buffer, |
1972 |
local_count << EVMS_VSECTOR_SIZE_SHIFT, |
1973 |
local_offset << EVMS_VSECTOR_SIZE_SHIFT); |
1974 |
if (rc < 0) { |
1975 |
rc = -rc; |
1976 |
goto out; |
1977 |
} |
1978 |
|
1979 |
/* Copy the data back to the caller's buffer. */ |
1980 |
if (!aligned) { |
1981 |
memcpy(buffer, local_buffer + |
1982 |
((offset - local_offset) << EVMS_VSECTOR_SIZE_SHIFT), |
1983 |
count << EVMS_VSECTOR_SIZE_SHIFT); |
1984 |
} |
1985 |
|
1986 |
/* Record this I/O in the cache. */ |
1987 |
write_to_cache(disk, offset, count, buffer); |
1988 |
rc = 0; |
1989 |
|
1990 |
out: |
1991 |
if (!aligned) { |
1992 |
free(local_buffer); |
1993 |
} |
1994 |
LOG_EXIT_INT(rc); |
1995 |
return rc; |
1996 |
} |
1997 |
|
1998 |
/** |
1999 |
* LD_write |
2000 |
**/ |
2001 |
static int LD_write(storage_object_t * disk, |
2002 |
lsn_t offset, |
2003 |
sector_count_t count, |
2004 |
void * buffer) |
2005 |
{ |
2006 |
void * local_buffer = NULL; |
2007 |
lsn_t local_offset; |
2008 |
sector_count_t local_count; |
2009 |
local_disk_t * ld = disk->private_data; |
2010 |
int rc, align_size, aligned = FALSE; |
2011 |
|
2012 |
LOG_ENTRY(); |
2013 |
LOG_DEBUG("Write disk:%s offset:%"PRIu64" count:%"PRIu64"\n", |
2014 |
disk->name, offset, count); |
2015 |
|
2016 |
if (offset + count > disk->size) { |
2017 |
LOG_ERROR("Write request past end of disk.\n"); |
2018 |
rc = EINVAL; |
2019 |
goto out; |
2020 |
} |
2021 |
|
2022 |
/* Make sure the disk is open. */ |
2023 |
rc = open_dev(disk); |
2024 |
if (rc) { |
2025 |
goto out; |
2026 |
} |
2027 |
|
2028 |
/* Get the alignment restriction for O_DIRECT. */ |
2029 |
align_size = get_alignment_size(disk); |
2030 |
|
2031 |
/* Check if the supplied buffer, offset, and count |
2032 |
* are valid for the alignment restrictions. |
2033 |
*/ |
2034 |
rc = check_alignment(align_size, offset, count, buffer); |
2035 |
if (rc) { |
2036 |
/* Get a data buffer aligned with this restriction. */ |
2037 |
rc = get_aligned_buffer(offset, count, align_size, &local_offset, |
2038 |
&local_count, &local_buffer); |
2039 |
if (rc) { |
2040 |
goto out; |
2041 |
} |
2042 |
} else { |
2043 |
aligned = TRUE; |
2044 |
local_offset = offset; |
2045 |
local_count = count; |
2046 |
local_buffer = buffer; |
2047 |
} |
2048 |
|
2049 |
if (local_count != count) { |
2050 |
rc = EngFncs->read_object(disk, ld->fd, local_buffer, |
2051 |
local_count << EVMS_VSECTOR_SIZE_SHIFT, |
2052 |
local_offset << EVMS_VSECTOR_SIZE_SHIFT); |
2053 |
if (rc < 0) { |
2054 |
rc = -rc; |
2055 |
goto out; |
2056 |
} |
2057 |
} |
2058 |
|
2059 |
/* Put user data at the right place in the buffer */ |
2060 |
if (!aligned) { |
2061 |
memcpy(local_buffer + |
2062 |
((offset - local_offset) << EVMS_VSECTOR_SIZE_SHIFT), |
2063 |
buffer, count << EVMS_VSECTOR_SIZE_SHIFT); |
2064 |
} |
2065 |
|
2066 |
/* Send the write to the engine. */ |
2067 |
rc = EngFncs->write_object(disk, ld->fd, local_buffer, |
2068 |
local_count << EVMS_VSECTOR_SIZE_SHIFT, |
2069 |
local_offset << EVMS_VSECTOR_SIZE_SHIFT); |
2070 |
if (rc < 0) { |
2071 |
rc = -rc; |
2072 |
goto out; |
2073 |
} |
2074 |
|
2075 |
/* The cache is too simple to do real caching. It's really only a read |
2076 |
* cache. A write, which should not happen during discovery, means the |
2077 |
* contents of the cache may not be up to date. Purge the cache and |
2078 |
* start caching all over again. |
2079 |
*/ |
2080 |
purge_cache(); |
2081 |
rc = 0; |
2082 |
|
2083 |
out: |
2084 |
if (!aligned) { |
2085 |
free(local_buffer); |
2086 |
} |
2087 |
LOG_EXIT_INT(rc); |
2088 |
return rc; |
2089 |
} |
2090 |
|
2091 |
/** |
2092 |
* LD_discard |
2093 |
* |
2094 |
* We don't expect to get called on this API. Just like commit. |
2095 |
**/ |
2096 |
static int LD_discard(list_anchor_t disks) |
2097 |
{ |
2098 |
LOG_ENTRY(); |
2099 |
LOG_EXIT_INT(0); |
2100 |
return 0; |
2101 |
} |
2102 |
|
2103 |
/** |
2104 |
* LD_add_sectors_to_kill_list |
2105 |
**/ |
2106 |
static int LD_add_sectors_to_kill_list(storage_object_t * disk, |
2107 |
lsn_t lsn, |
2108 |
sector_count_t count) |
2109 |
{ |
2110 |
int rc; |
2111 |
LOG_ENTRY(); |
2112 |
|
2113 |
if (lsn + count > disk->size) { |
2114 |
LOG_ERROR("Kill-sectors request past end of disk %s.\n", |
2115 |
disk->name); |
2116 |
rc = EINVAL; |
2117 |
} else { |
2118 |
rc = EngFncs->add_sectors_to_kill_list(disk, lsn, count); |
2119 |
} |
2120 |
|
2121 |
LOG_EXIT_INT(rc); |
2122 |
return rc; |
2123 |
} |
2124 |
|
2125 |
/** |
2126 |
* LD_commit_changes |
2127 |
* |
2128 |
* Disk manager doesn't do anything during commit. Just return success. |
2129 |
**/ |
2130 |
static int LD_commit_changes(storage_object_t * disk, commit_phase_t phase) |
2131 |
{ |
2132 |
LOG_ENTRY(); |
2133 |
LOG_EXIT_INT(0); |
2134 |
return 0; |
2135 |
} |
2136 |
|
2137 |
/** |
2138 |
* LD_get_info |
2139 |
* |
2140 |
* Return information about this disk to display to the user. |
2141 |
**/ |
2142 |
static int LD_get_info(storage_object_t * disk, |
2143 |
char * name, |
2144 |
extended_info_array_t ** info) |
2145 |
{ |
2146 |
local_disk_t * ld = disk->private_data; |
2147 |
int rc = EINVAL; |
2148 |
|
2149 |
LOG_ENTRY(); |
2150 |
|
2151 |
*info = NULL; |
2152 |
|
2153 |
if (!name) { |
2154 |
rc = get_basic_info(disk, info); |
2155 |
} else if (!strncasecmp(name, "Type", 4)) { |
2156 |
if (ld->flags & LD_FLAG_IDE) { |
2157 |
rc = get_ide_info(disk, info); |
2158 |
} else if (ld->flags & LD_FLAG_SCSI) { |
2159 |
rc = get_scsi_info(disk, info); |
2160 |
} |
2161 |
} |
2162 |
|
2163 |
LOG_EXIT_INT(rc); |
2164 |
return rc; |
2165 |
} |
2166 |
|
2167 |
/** |
2168 |
* LD_get_plugin_info |
2169 |
* |
2170 |
* Returns plug-in specific information |
2171 |
**/ |
2172 |
static int LD_get_plugin_info(char * descriptor_name, |
2173 |
extended_info_array_t ** info) |
2174 |
{ |
2175 |
int rc = EINVAL; |
2176 |
extended_info_array_t * Info; |
2177 |
char version_string[64]; |
2178 |
char required_engine_api_version_string[64]; |
2179 |
char required_plugin_api_version_string[64]; |
2180 |
|
2181 |
LOG_ENTRY(); |
2182 |
|
2183 |
if (!info) { |
2184 |
goto out; |
2185 |
} |
2186 |
*info = NULL; |
2187 |
|
2188 |
if (descriptor_name) { |
2189 |
goto out; |
2190 |
} |
2191 |
|
2192 |
Info = EngFncs->engine_alloc(sizeof(extended_info_array_t) + |
2193 |
6 * sizeof(extended_info_t)); |
2194 |
if (!Info) { |
2195 |
rc = ENOMEM; |
2196 |
goto out; |
2197 |
} |
2198 |
|
2199 |
Info->count = 6; |
2200 |
|
2201 |
sprintf(version_string, "%d.%d.%d", |
2202 |
MAJOR_VERSION, MINOR_VERSION, PATCH_LEVEL); |
2203 |
|
2204 |
sprintf(required_engine_api_version_string, "%d.%d.%d", |
2205 |
my_plugin_record->required_engine_api_version.major, |
2206 |
my_plugin_record->required_engine_api_version.minor, |
2207 |
my_plugin_record->required_engine_api_version.patchlevel); |
2208 |
|
2209 |
sprintf(required_plugin_api_version_string, "%d.%d.%d", |
2210 |
my_plugin_record->required_plugin_api_version.plugin.major, |
2211 |
my_plugin_record->required_plugin_api_version.plugin.minor, |
2212 |
my_plugin_record->required_plugin_api_version.plugin.patchlevel); |
2213 |
|
2214 |
Info->info[0].name = EngFncs->engine_strdup("Short Name"); |
2215 |
Info->info[0].title = EngFncs->engine_strdup(_("Short Name")); |
2216 |
Info->info[0].desc = EngFncs->engine_strdup(_("A short name given to this plug-in")); |
2217 |
Info->info[0].type = EVMS_Type_String; |
2218 |
Info->info[0].value.s = EngFncs->engine_strdup(my_plugin_record->short_name); |
2219 |
|
2220 |
Info->info[1].name = EngFncs->engine_strdup("Long Name"); |
2221 |
Info->info[1].title = EngFncs->engine_strdup(_("Long Name")); |
2222 |
Info->info[1].desc = EngFncs->engine_strdup(_("A longer, more descriptive name for this plug-in")); |
2223 |
Info->info[1].type = EVMS_Type_String; |
2224 |
Info->info[1].value.s = EngFncs->engine_strdup(my_plugin_record->long_name); |
2225 |
|
2226 |
Info->info[2].name = EngFncs->engine_strdup("Type"); |
2227 |
Info->info[2].title = EngFncs->engine_strdup(_("Plug-in Type")); |
2228 |
Info->info[2].desc = EngFncs->engine_strdup(_("There are various types of plug-ins, each responsible for some kind of storage object or logical volume.")); |
2229 |
Info->info[2].type = EVMS_Type_String; |
2230 |
Info->info[2].value.s = EngFncs->engine_strdup(_("Device Manager")); |
2231 |
|
2232 |
Info->info[3].name = EngFncs->engine_strdup("Version"); |
2233 |
Info->info[3].title = EngFncs->engine_strdup(_("Plug-in Version")); |
2234 |
Info->info[3].desc = EngFncs->engine_strdup(_("Version number of this plug-in")); |
2235 |
Info->info[3].type = EVMS_Type_String; |
2236 |
Info->info[3].value.s = EngFncs->engine_strdup(version_string); |
2237 |
|
2238 |
Info->info[4].name = EngFncs->engine_strdup("Required Engine Services Version"); |
2239 |
Info->info[4].title = EngFncs->engine_strdup(_("Required Engine Services Version")); |
2240 |
Info->info[4].desc = EngFncs->engine_strdup(_("Version of the Engine services that this plug-in requires. " |
2241 |
"It will not run on older versions of the Engine services.")); |
2242 |
Info->info[4].type = EVMS_Type_String; |
2243 |
Info->info[4].value.s = EngFncs->engine_strdup(required_engine_api_version_string); |
2244 |
|
2245 |
Info->info[5].name = EngFncs->engine_strdup("Required Plug-in API Version"); |
2246 |
Info->info[5].title = EngFncs->engine_strdup(_("Required Plug-in API Version")); |
2247 |
Info->info[5].desc = EngFncs->engine_strdup(_("Version of the Engine plug-in API that this plug-in requires. " |
2248 |
"It will not run on older versions of the Engine plug-in API.")); |
2249 |
Info->info[5].type = EVMS_Type_String; |
2250 |
Info->info[5].value.s = EngFncs->engine_strdup(required_plugin_api_version_string); |
2251 |
|
2252 |
*info = Info; |
2253 |
rc = 0; |
2254 |
|
2255 |
out: |
2256 |
LOG_EXIT_INT(rc); |
2257 |
return rc; |
2258 |
} |
2259 |
|
2260 |
|
2261 |
static int LD_get_plugin_functions(storage_object_t * object, |
2262 |
function_info_array_t * * actions) |
2263 |
{ |
2264 |
LOG_ENTRY(); |
2265 |
|
2266 |
/* |
2267 |
* The Local Disk Manager has plug-in functions, but they are not |
2268 |
* available for the asking. Those who want to use them must know |
2269 |
* what they are and call the plug-in function directly which will |
2270 |
* be processed by LD_plugin_function() below. |
2271 |
*/ |
2272 |
|
2273 |
LOG_EXIT_INT(ENOSYS); |
2274 |
return ENOSYS; |
2275 |
} |
2276 |
|
2277 |
static int LD_plugin_function(storage_object_t * object, |
2278 |
task_action_t action, |
2279 |
list_anchor_t objects, |
2280 |
option_array_t * options) |
2281 |
{ |
2282 |
int rc = 0; |
2283 |
|
2284 |
LOG_ENTRY(); |
2285 |
|
2286 |
switch (action) { |
2287 |
case LDM_Start_Caching: |
2288 |
LOG_DEBUG("Start caching\n"); |
2289 |
initialize_cache(); |
2290 |
break; |
2291 |
|
2292 |
case LDM_Stop_Caching: |
2293 |
LOG_DEBUG("Stop caching\n"); |
2294 |
destroy_cache(); |
2295 |
break; |
2296 |
|
2297 |
case LDM_Open_Disk: |
2298 |
if (object->plugin == my_plugin_record) { |
2299 |
LOG_DEBUG("Open disk %s\n", object->name); |
2300 |
rc = open_dev(object); |
2301 |
|
2302 |
} else { |
2303 |
LOG_ERROR("%s is not managed by %s.\n", |
2304 |
object->name, my_plugin_record->short_name); |
2305 |
rc = EINVAL; |
2306 |
} |
2307 |
break; |
2308 |
|
2309 |
case LDM_Close_Disk: |
2310 |
if (object->plugin == my_plugin_record) { |
2311 |
LOG_DEBUG("Close disk %s\n", object->name); |
2312 |
close_dev(object); |
2313 |
|
2314 |
} else { |
2315 |
LOG_ERROR("%s is not managed by %s.\n", |
2316 |
object->name, my_plugin_record->short_name); |
2317 |
rc = EINVAL; |
2318 |
} |
2319 |
break; |
2320 |
|
2321 |
default: |
2322 |
LOG_ERROR("%d is not a valid function code.\n", action); |
2323 |
rc = EINVAL; |
2324 |
} |
2325 |
|
2326 |
LOG_EXIT_INT(rc); |
2327 |
return rc; |
2328 |
} |
2329 |
|
2330 |
|
2331 |
static int LD_backup_metadata(storage_object_t * disk) { |
2332 |
|
2333 |
int rc; |
2334 |
|
2335 |
LOG_ENTRY(); |
2336 |
|
2337 |
if (disk->plugin->id != EVMS_DISK_PLUGIN_ID) { |
2338 |
LOG_ERROR("I don't own object %s.\n", disk->name); |
2339 |
LOG_EXIT_INT(EINVAL); |
2340 |
return EINVAL; |
2341 |
} |
2342 |
|
2343 |
rc = EngFncs->save_metadata(disk->name, NULL, 0, 0, NULL); |
2344 |
|
2345 |
LOG_EXIT_INT(rc); |
2346 |
return rc; |
2347 |
} |
2348 |
|
2349 |
|
2350 |
static plugin_functions_t ft_sysfs = { |
2351 |
.setup_evms_plugin = LD_setup, |
2352 |
.cleanup_evms_plugin = LD_cleanup, |
2353 |
.discover = LD_discover, |
2354 |
.discard = LD_discard, |
2355 |
.add_sectors_to_kill_list = LD_add_sectors_to_kill_list, |
2356 |
.commit_changes = LD_commit_changes, |
2357 |
.get_info = LD_get_info, |
2358 |
.get_plugin_info = LD_get_plugin_info, |
2359 |
.read = LD_read, |
2360 |
.write = LD_write, |
2361 |
.get_plugin_functions = LD_get_plugin_functions, |
2362 |
.plugin_function = LD_plugin_function, |
2363 |
.backup_metadata = LD_backup_metadata |
2364 |
}; |
2365 |
|
2366 |
plugin_record_t LD_Plugin = { |
2367 |
.id = EVMS_DISK_PLUGIN_ID, |
2368 |
.version = { |
2369 |
.major = MAJOR_VERSION, |
2370 |
.minor = MINOR_VERSION, |
2371 |
.patchlevel = PATCH_LEVEL |
2372 |
}, |
2373 |
.required_engine_api_version = { |
2374 |
.major = 15, |
2375 |
.minor = 0, |
2376 |
.patchlevel = 0 |
2377 |
}, |
2378 |
.required_plugin_api_version = { |
2379 |
.plugin = { |
2380 |
.major = 13, |
2381 |
.minor = 1, |
2382 |
.patchlevel = 0 |
2383 |
} |
2384 |
}, |
2385 |
.short_name = EVMS_DISK_PLUGIN_SHORT_NAME, |
2386 |
.long_name = EVMS_DISK_PLUGIN_LONG_NAME, |
2387 |
.oem_name = EVMS_IBM_OEM_NAME, |
2388 |
.functions = { |
2389 |
.plugin = &ft_sysfs |
2390 |
}, |
2391 |
.container_functions = NULL |
2392 |
}; |
2393 |
|
2394 |
plugin_record_t * my_plugin_record = &LD_Plugin; |
2395 |
|
2396 |
plugin_record_t * evms_plugin_records[] = { &LD_Plugin, |
2397 |
NULL }; |
2398 |
|