1 /***************************************************************************
2  *
3  * probe-storage.c : Probe for storage devices
4  *
5  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
6  * Use is subject to license terms.
7  *
8  * Licensed under the Academic Free License version 2.1
9  *
10  **************************************************************************/
11 
12 #ifdef HAVE_CONFIG_H
13 #  include <config.h>
14 #endif
15 
16 #include <errno.h>
17 #include <string.h>
18 #include <strings.h>
19 #include <ctype.h>
20 #include <stdlib.h>
21 #include <stdio.h>
22 #include <sys/ioctl.h>
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <fcntl.h>
26 #include <unistd.h>
27 #include <sys/mnttab.h>
28 #include <sys/fdio.h>
29 #include <sys/scsi/scsi.h>
30 #include <sys/vtoc.h>
31 #include <sys/efi_partition.h>
32 #include <priv.h>
33 
34 #include <libhal.h>
35 #include <cdutils.h>
36 #include <fsutils.h>
37 #include <logger.h>
38 
39 /** Check if a filesystem on a special device file is mounted
40  *
41  *  @param  device_file         Special device file, e.g. /dev/cdrom
42  *  @return                     TRUE iff there is a filesystem system mounted
43  *                              on the special device file
44  */
45 static dbus_bool_t
is_mounted(const char * device_file)46 is_mounted (const char *device_file)
47 {
48 	FILE *f;
49 	dbus_bool_t rc = FALSE;
50 	struct mnttab mp;
51 	struct mnttab mpref;
52 
53 	if ((f = fopen ("/etc/mnttab", "r")) == NULL)
54 		return rc;
55 
56 	bzero(&mp, sizeof (mp));
57 	bzero(&mpref, sizeof (mpref));
58 	mpref.mnt_special = (char *)device_file;
59 	if (getmntany(f, &mp, &mpref) == 0) {
60 		rc = TRUE;
61 	}
62 
63 	fclose (f);
64 	return rc;
65 }
66 
67 static int
get_cdrom_properties_walker(void * arg,int profile,boolean_t is_current)68 get_cdrom_properties_walker (void *arg, int profile, boolean_t is_current)
69 {
70 	LibHalChangeSet	*cs = (LibHalChangeSet *)arg;
71 
72 	switch (profile) {
73 	case 0x09:
74 		libhal_changeset_set_property_bool (cs, "storage.cdrom.cdr", TRUE);
75 		break;
76 	case 0x0a:
77 		libhal_changeset_set_property_bool (cs, "storage.cdrom.cdrw", TRUE);
78 		break;
79 	case 0x10:
80 		libhal_changeset_set_property_bool (cs, "storage.cdrom.dvd", TRUE);
81 		break;
82 	case 0x11:
83 		libhal_changeset_set_property_bool (cs, "storage.cdrom.dvdr", TRUE);
84 		break;
85 	case 0x12:
86 		libhal_changeset_set_property_bool (cs, "storage.cdrom.dvdram", TRUE);
87 		break;
88 	case 0x13:
89 		libhal_changeset_set_property_bool (cs, "storage.cdrom.dvdrw", TRUE);
90 		break;
91 	case 0x14:
92 		libhal_changeset_set_property_bool (cs, "storage.cdrom.dvdrw", TRUE);
93 		break;
94 	case 0x1a:
95 		libhal_changeset_set_property_bool (cs, "storage.cdrom.dvdplusrw", TRUE);
96 		break;
97 	case 0x1b:
98 		libhal_changeset_set_property_bool (cs, "storage.cdrom.dvdplusr", TRUE);
99 		break;
100 	case 0x2b:
101 		libhal_changeset_set_property_bool (cs, "storage.cdrom.dvdplusrdl", TRUE);
102 		break;
103 	case 0x40:
104 		libhal_changeset_set_property_bool (cs, "storage.cdrom.bd", TRUE);
105 		break;
106 	case 0x41:
107 	case 0x42:
108 		libhal_changeset_set_property_bool (cs, "storage.cdrom.bdr", TRUE);
109 		break;
110 	case 0x43:
111 		libhal_changeset_set_property_bool (cs, "storage.cdrom.bdre", TRUE);
112 		break;
113 	case 0x50:
114 		libhal_changeset_set_property_bool (cs, "storage.cdrom.hddvd", TRUE);
115 		break;
116 	case 0x51:
117 		libhal_changeset_set_property_bool (cs, "storage.cdrom.hddvdr", TRUE);
118 		break;
119 	case 0x52:
120 		libhal_changeset_set_property_bool (cs, "storage.cdrom.hddvdrw", TRUE);
121 		break;
122 	}
123 
124 	return CDUTIL_WALK_CONTINUE;
125 }
126 
127 #define	WSPLEN	64
128 
129 static void
get_cdrom_properties(int fd,LibHalChangeSet * cs)130 get_cdrom_properties (int fd, LibHalChangeSet *cs)
131 {
132 	DBusError error;
133 	int capabilities;
134 	int read_speed, write_speed;
135 	intlist_t *write_speeds, *write_speeds_mem, *sp;
136 	int n_wspeeds;
137 	char **wspeeds;
138 	char *wspeeds_mem;
139 	int i;
140 
141 	libhal_changeset_set_property_bool (cs, "storage.cdrom.cdr", FALSE);
142 	libhal_changeset_set_property_bool (cs, "storage.cdrom.cdrw", FALSE);
143 	libhal_changeset_set_property_bool (cs, "storage.cdrom.dvd", FALSE);
144 	libhal_changeset_set_property_bool (cs, "storage.cdrom.dvdr", FALSE);
145 	libhal_changeset_set_property_bool (cs, "storage.cdrom.dvdrw", FALSE);
146 	libhal_changeset_set_property_bool (cs, "storage.cdrom.dvdram", FALSE);
147 	libhal_changeset_set_property_bool (cs, "storage.cdrom.dvdplusr", FALSE);
148 	libhal_changeset_set_property_bool (cs, "storage.cdrom.dvdplusrw", FALSE);
149 	libhal_changeset_set_property_bool (cs, "storage.cdrom.dvdplusrdl", FALSE);
150 	libhal_changeset_set_property_bool (cs, "storage.cdrom.bd", FALSE);
151 	libhal_changeset_set_property_bool (cs, "storage.cdrom.bdr", FALSE);
152 	libhal_changeset_set_property_bool (cs, "storage.cdrom.bdre", FALSE);
153 	libhal_changeset_set_property_bool (cs, "storage.cdrom.hddvd", FALSE);
154 	libhal_changeset_set_property_bool (cs, "storage.cdrom.hddvdr", FALSE);
155 	libhal_changeset_set_property_bool (cs, "storage.cdrom.hddvdrw", FALSE);
156 
157 	walk_profiles(fd, get_cdrom_properties_walker, cs);
158 
159 	/* XXX */
160 	libhal_changeset_set_property_bool (cs, "storage.cdrom.support_media_changed", TRUE);
161 
162 	get_read_write_speeds(fd, &read_speed, &write_speed, &write_speeds, &n_wspeeds, &write_speeds_mem);
163 
164 	libhal_changeset_set_property_int (cs, "storage.cdrom.read_speed", read_speed);
165 	libhal_changeset_set_property_int (cs, "storage.cdrom.write_speed", write_speed);
166 
167 	if (n_wspeeds <= 0) {
168 		wspeeds_mem = NULL;
169 		libhal_changeset_set_property_strlist (cs, "storage.cdrom.write_speeds", (const char **)&wspeeds_mem);
170 		return;
171 	}
172 	if ((wspeeds = (char **)calloc(n_wspeeds + 1, sizeof (char *))) == NULL) {
173 		free (write_speeds_mem);
174 		return;
175 	}
176 	if ((wspeeds_mem = (char *)calloc(n_wspeeds, WSPLEN)) == NULL) {
177 		free (wspeeds);
178 		free (write_speeds_mem);
179 		return;
180 	}
181 	for (i = 0; i < n_wspeeds; i++) {
182 		wspeeds[i] = &wspeeds_mem[i * WSPLEN];
183 	}
184 
185 	for (sp = write_speeds, i = 0; sp != NULL; sp = sp->next, i++) {
186 		snprintf (wspeeds[i], WSPLEN, "%d", sp->val);
187 	}
188 	libhal_changeset_set_property_strlist (cs, "storage.cdrom.write_speeds", (const char **)wspeeds);
189 
190 	free (wspeeds);
191 	free (wspeeds_mem);
192 	free (write_speeds_mem);
193 }
194 
195 /*
196  * Return a copy of a string without trailing spaces. If 'len' is non-zero,
197  * it specifies max length, otherwise the string must be null-terminated.
198  */
199 char *
rtrim_copy(char * src,int len)200 rtrim_copy(char *src, int len)
201 {
202 	char	*dst, *p;
203 
204 	if (len == 0) {
205 		len = strlen(src);
206 	}
207 	if ((dst = calloc(1, len + 1)) != NULL) {
208 		strncpy(dst, src, len);
209 		p = dst + len - 1;
210 		while ((p >= dst) && (isspace(*p))) {
211 			*p-- = '\0';
212 		}
213 	}
214 	return (dst);
215 }
216 
217 static void
get_disk_properties(int fd,LibHalChangeSet * cs)218 get_disk_properties (int fd, LibHalChangeSet *cs)
219 {
220 	struct scsi_inquiry inq;
221 	struct uscsi_cmd ucmd;
222 	union scsi_cdb  cdb;
223 	int		status;
224 	char		*s;
225 
226 	/* INQUIRY */
227 	(void) memset((void *) &inq, 0, sizeof (inq));
228 	(void) memset((void *) &ucmd, 0, sizeof (ucmd));
229 	(void) memset((void *) &cdb, 0, sizeof (union scsi_cdb));
230 	cdb.scc_cmd = SCMD_INQUIRY;
231 	FORMG0COUNT(&cdb, sizeof (inq));
232 	ucmd.uscsi_cdb = (caddr_t) & cdb;
233 	ucmd.uscsi_cdblen = CDB_GROUP0;
234 	ucmd.uscsi_bufaddr = (caddr_t) & inq;
235 	ucmd.uscsi_buflen = sizeof (inq);
236 	ucmd.uscsi_timeout = 30;
237 	ucmd.uscsi_flags = USCSI_READ;
238 	status = ioctl(fd, USCSICMD, &ucmd);
239 	if (status || ucmd.uscsi_status) {
240 		return;
241 	}
242 
243 	if ((s = rtrim_copy(inq.inq_vid, sizeof (inq.inq_vid))) != NULL) {
244 		libhal_changeset_set_property_string (cs, "storage.vendor", s);
245 		free(s);
246 	}
247 	if ((s = rtrim_copy(inq.inq_pid, sizeof (inq.inq_pid))) != NULL) {
248 		libhal_changeset_set_property_string (cs, "storage.model", s);
249 		free(s);
250 	}
251 	if ((s = rtrim_copy(inq.inq_revision, sizeof (inq.inq_revision))) != NULL) {
252 		libhal_changeset_set_property_string (cs, "storage.firmware_revision", s);
253 		free(s);
254 	}
255 	if ((s = rtrim_copy(inq.inq_serial, sizeof (inq.inq_serial))) != NULL) {
256 		libhal_changeset_set_property_string (cs, "storage.serial", s);
257 		free(s);
258 	}
259 }
260 
261 /*
262  * returns TRUE if diskette is inserted.
263  * also returns write protection status.
264  */
265 static dbus_bool_t
check_floppy(int fd,dbus_bool_t * wprot)266 check_floppy(int fd, dbus_bool_t *wprot)
267 {
268 	int	chg;
269 
270 	if ((ioctl(fd, FDGETCHANGE, &chg) == 0) && !(chg & FDGC_CURRENT)) {
271 		*wprot = ((chg & FDGC_CURWPROT) != 0);
272 		return (TRUE);
273 	} else {
274 		return (FALSE);
275 	}
276 }
277 
278 void
drop_privileges()279 drop_privileges ()
280 {
281 	priv_set_t *pPrivSet = NULL;
282 	priv_set_t *lPrivSet = NULL;
283 
284 	/*
285 	 * Start with the 'basic' privilege set and then remove any
286 	 * of the 'basic' privileges that will not be needed.
287 	 */
288 	if ((pPrivSet = priv_str_to_set("basic", ",", NULL)) == NULL) {
289 		return;
290 	}
291 
292 	/* Clear privileges we will not need from the 'basic' set */
293 	(void) priv_delset(pPrivSet, PRIV_FILE_LINK_ANY);
294 	(void) priv_delset(pPrivSet, PRIV_PROC_INFO);
295 	(void) priv_delset(pPrivSet, PRIV_PROC_SESSION);
296 	(void) priv_delset(pPrivSet, PRIV_PROC_EXEC);
297 	(void) priv_delset(pPrivSet, PRIV_PROC_FORK);
298 
299 	/* for uscsi */
300 	(void) priv_addset(pPrivSet, PRIV_SYS_DEVICES);
301 
302 	/* to open logindevperm'd devices */
303 	(void) priv_addset(pPrivSet, PRIV_FILE_DAC_READ);
304 
305 	/* Set the permitted privilege set. */
306 	if (setppriv(PRIV_SET, PRIV_PERMITTED, pPrivSet) != 0) {
307 		return;
308 	}
309 
310 	/* Clear the limit set. */
311 	if ((lPrivSet = priv_allocset()) == NULL) {
312 		return;
313 	}
314 
315 	priv_emptyset(lPrivSet);
316 
317 	if (setppriv(PRIV_SET, PRIV_LIMIT, lPrivSet) != 0) {
318 		return;
319 	}
320 
321 	priv_freeset(lPrivSet);
322 }
323 
324 int
main(int argc,char * argv[])325 main (int argc, char *argv[])
326 {
327 	int ret = 1;
328 	int fd = -1;
329 	int rfd = -1;
330 	char *udi;
331 	char *device_file;
332 	char *raw_device_file;
333 	LibHalContext *ctx = NULL;
334 	DBusError error;
335 	char *drive_type;
336 	dbus_bool_t is_cdrom;
337 	dbus_bool_t is_floppy;
338 	struct dk_minfo minfo;
339 	int rdonly;
340 	unsigned int block_size = 512;
341 	dbus_bool_t only_check_for_media;
342 	int got_media = FALSE;
343 	dbus_bool_t is_write_protected = FALSE;
344 	dbus_bool_t is_mbr = FALSE;
345 	dbus_bool_t is_smi = FALSE;
346 	dbus_bool_t is_gpt = FALSE;
347 	dbus_bool_t is_partitioned = FALSE;
348 	dbus_bool_t vtoc_slices = FALSE;
349 	int dos_cnt = 0;
350 	const char *scheme = "";
351 	struct extvtoc vtoc;
352 	dk_gpt_t *gpt;
353 	LibHalChangeSet *cs = NULL;
354 
355 	if ((udi = getenv ("UDI")) == NULL)
356 		goto out;
357 	if ((device_file = getenv ("HAL_PROP_BLOCK_DEVICE")) == NULL)
358 		goto out;
359 	if ((raw_device_file = getenv ("HAL_PROP_BLOCK_SOLARIS_RAW_DEVICE")) == NULL)
360 		goto out;
361 	if ((drive_type = getenv ("HAL_PROP_STORAGE_DRIVE_TYPE")) == NULL)
362 		goto out;
363 
364 	drop_privileges ();
365 
366 	setup_logger ();
367 
368 	if (argc == 2 && strcmp (argv[1], "--only-check-for-media") == 0)
369 		only_check_for_media = TRUE;
370 	else
371 		only_check_for_media = FALSE;
372 
373 	is_cdrom = (strcmp (drive_type, "cdrom") == 0);
374 	is_floppy = (strcmp (drive_type, "floppy") == 0);
375 
376 	dbus_error_init (&error);
377 	if ((ctx = libhal_ctx_init_direct (&error)) == NULL)
378 		goto out;
379 
380 	if ((cs = libhal_device_new_changeset (udi)) == NULL) {
381 		HAL_DEBUG (("Cannot allocate changeset"));
382 		goto out;
383 	}
384 
385 	HAL_DEBUG (("Doing probe-storage for %s (drive_type %s) (udi=%s) (--only-check-for-media==%d)",
386 	     device_file, drive_type, udi, only_check_for_media));
387 
388 	if ((rfd = open (raw_device_file, O_RDONLY | O_NONBLOCK)) < 0) {
389 		HAL_DEBUG (("Cannot open %s: %s", raw_device_file, strerror (errno)));
390 		goto out;
391 	}
392 
393 	if (!only_check_for_media) {
394 		if (strcmp (drive_type, "cdrom") == 0) {
395 			get_cdrom_properties (rfd, cs);
396 		} else if (strcmp (drive_type, "disk") == 0) {
397 			get_disk_properties (rfd, cs);
398 		}
399 	}
400 
401 	ret = 0;
402 
403 	if (is_cdrom) {
404 		HAL_DEBUG (("Checking for optical disc on %s", raw_device_file));
405 		got_media = get_media_info(rfd, &minfo);
406 		if (!got_media) {
407 			goto out_cs;
408 		}
409 		block_size = minfo.dki_lbsize;
410 		/* XXX */
411 		is_write_protected = TRUE;
412 	} else if (is_floppy) {
413 		HAL_DEBUG (("Checking for floppy on %s", raw_device_file));
414 		if (check_floppy(rfd, &is_write_protected)) {
415 			got_media = TRUE;
416 		}
417 		/* don't look for partitions on floppy */
418 		goto out_cs;
419 	} else {
420 		got_media = get_media_info(rfd, &minfo);
421 		if (!got_media) {
422 			goto out_cs;
423 		}
424 		block_size = minfo.dki_lbsize;
425 		if ((ioctl(rfd, DKIOCREADONLY, &rdonly) == 0) && rdonly) {
426 			is_write_protected = TRUE;
427 		}
428 	}
429 
430 	HAL_DEBUG (("Checking for partitions on %s", device_file));
431 
432 	if ((fd = open (device_file, O_RDONLY | O_NONBLOCK)) < 0) {
433 		HAL_DEBUG (("Cannot open %s: %s", device_file, strerror (errno)));
434 		goto out_cs;
435 	}
436 
437 	dos_cnt = get_num_dos_drives(fd, block_size);
438 	is_mbr = (dos_cnt > 0);
439 	if (is_mbr) {
440 		scheme = "mbr";
441 	}
442 	if (read_extvtoc(rfd, &vtoc) >= 0) {
443 		if (!vtoc_one_slice_entire_disk(&vtoc)) {
444 			is_smi = TRUE;
445 			if (!is_mbr) {
446 				/* smi within mbr partition is okay */
447 				scheme = "smi";
448 			}
449 			vtoc_slices = TRUE;
450 		}
451 	} else if (!is_cdrom && (efi_alloc_and_read(rfd, &gpt) >= 0)) {
452 		/*
453 		 * Note: for some reason efi_read takes very long on cdroms.
454 		 * Needs more investigation, skip gpt on cdrom for now.
455 		 */
456 		is_gpt = TRUE;
457 		scheme = "gpt";
458 		efi_free(gpt);
459 	}
460 
461 out_cs:
462 	is_partitioned = is_mbr || is_smi || is_gpt;
463 	libhal_changeset_set_property_bool (cs, "storage.no_partitions_hint", !is_partitioned);
464 	libhal_changeset_set_property_bool (cs, "block.no_partitions", !is_partitioned);
465 	libhal_changeset_set_property_string (cs, "storage.partitioning_scheme", scheme);
466 	libhal_changeset_set_property_bool (cs, "storage.solaris.vtoc_slices", vtoc_slices);
467 	libhal_changeset_set_property_int (cs, "storage.solaris.num_dos_partitions", dos_cnt);
468 	/* XXX should only set for removable drives */
469 	libhal_changeset_set_property_bool (cs, "storage.removable.media_available", got_media);
470 	libhal_changeset_set_property_bool (cs, "storage.removable.solaris.read_only", is_write_protected);
471 
472 	libhal_device_commit_changeset (ctx, cs, &error);
473 
474 out:
475 	if (cs != NULL) {
476 		libhal_device_free_changeset (cs);
477 	}
478 	if (fd >= 0) {
479 		close (fd);
480 	}
481 	if (rfd >= 0) {
482 		close (rfd);
483 	}
484 	if (ctx != NULL) {
485 		if (dbus_error_is_set(&error)) {
486 			dbus_error_free (&error);
487 		}
488 		libhal_ctx_shutdown (ctx, &error);
489 		libhal_ctx_free (ctx);
490 	}
491 
492 	return ret;
493 }
494