1 /***************************************************************************
4 * Sat Apr 19 11:12:53 2008
5 * Copyright 2008 John Stebbins
6 * <john at stebbins dot name>
7 ****************************************************************************/
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Library General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor Boston, MA 02110-1301, USA
25 // Well, I waisted a bit of time on this. It seems libhb has a function for
26 // this that I hadn't discovered yet. hb_dvd_name().
28 // I borrowed most of this from the udev utility vol_id
29 // Here is the authors copyright.
31 * volume_id - reads filesystem label and uuid
33 * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org>
35 * This program is free software; you can redistribute it and/or modify it
36 * under the terms of the GNU General Public License as published by the
37 * Free Software Foundation version 2 of the License.
55 #define PACKED __attribute__((packed))
58 struct volume_descriptor {
59 struct descriptor_tag {
70 struct anchor_descriptor {
74 struct primary_descriptor {
85 struct volume_structure_descriptor {
91 #define VOLUME_ID_LABEL_SIZE 64
95 gchar label[VOLUME_ID_LABEL_SIZE+1];
104 #if (__BYTE_ORDER == __LITTLE_ENDIAN)
105 #define le16_to_cpu(x) (x)
106 #define le32_to_cpu(x) (x)
107 #define le64_to_cpu(x) (x)
108 #define be16_to_cpu(x) bswap_16(x)
109 #define be32_to_cpu(x) bswap_32(x)
110 #define cpu_to_le16(x) (x)
111 #define cpu_to_le32(x) (x)
112 #define cpu_to_be32(x) bswap_32(x)
113 #elif (__BYTE_ORDER == __BIG_ENDIAN)
114 #define le16_to_cpu(x) bswap_16(x)
115 #define le32_to_cpu(x) bswap_32(x)
116 #define le64_to_cpu(x) bswap_64(x)
117 #define be16_to_cpu(x) (x)
118 #define be32_to_cpu(x) (x)
119 #define cpu_to_le16(x) bswap_16(x)
120 #define cpu_to_le32(x) bswap_32(x)
121 #define cpu_to_be32(x) (x)
123 #endif /* __BYTE_ORDER */
125 #define UDF_VSD_OFFSET 0x8000
128 get_buffer(int fd, guint64 off, gsize len)
132 if (lseek(fd, off, SEEK_SET) < 0)
136 guint8 *buffer = g_malloc(len);
137 buf_len = read(fd, buffer, len);
147 set_unicode16(guint8 *str, gsize len, const guint8 *buf, gint endianess, gsize count)
153 for (ii = 0; ii + 2 <= count; ii += 2) {
155 c = (buf[ii+1] << 8) | buf[ii];
157 c = (buf[ii] << 8) | buf[ii+1];
161 } else if (c < 0x80) {
164 str[jj++] = (guint8) c;
165 } else if (c < 0x800) {
168 str[jj++] = (guint8) (0xc0 | (c >> 6));
169 str[jj++] = (guint8) (0x80 | (c & 0x3f));
173 str[jj++] = (guint8) (0xe0 | (c >> 12));
174 str[jj++] = (guint8) (0x80 | ((c >> 6) & 0x3f));
175 str[jj++] = (guint8) (0x80 | (c & 0x3f));
183 set_label_string(guint8 *str, const guint8 *buf, gsize count)
187 memcpy(str, buf, count);
190 /* remove trailing whitespace */
191 ii = strlen((gchar*)str);
194 if (!g_ascii_isspace(str[ii]))
201 probe_udf(udf_info_t *id)
203 struct volume_descriptor *vd;
204 struct volume_structure_descriptor *vsd;
213 vsd = (struct volume_structure_descriptor *) get_buffer(id->fd, off + UDF_VSD_OFFSET, 0x200);
217 if (memcmp(vsd->id, "NSR02", 5) == 0)
219 if (memcmp(vsd->id, "NSR03", 5) == 0)
221 if (memcmp(vsd->id, "BEA01", 5) == 0)
223 if (memcmp(vsd->id, "BOOT2", 5) == 0)
225 if (memcmp(vsd->id, "CD001", 5) == 0)
227 if (memcmp(vsd->id, "CDW02", 5) == 0)
229 if (memcmp(vsd->id, "TEA03", 5) == 0)
234 /* search the next VSD to get the logical block size of the volume */
235 for (bs = 0x800; bs < 0x8000; bs += 0x800) {
236 vsd = (struct volume_structure_descriptor *) get_buffer(id->fd, off + UDF_VSD_OFFSET + bs, 0x800);
239 if (vsd->id[0] != '\0')
245 /* search the list of VSDs for a NSR descriptor */
246 for (b = 0; b < 64; b++) {
247 vsd = (struct volume_structure_descriptor *) get_buffer(id->fd, off + UDF_VSD_OFFSET + (b * bs), 0x800);
251 if (vsd->id[0] == '\0')
253 if (memcmp(vsd->id, "NSR02", 5) == 0)
255 if (memcmp(vsd->id, "NSR03", 5) == 0)
261 /* read anchor volume descriptor */
262 vd = (struct volume_descriptor *) get_buffer(id->fd, off + (256 * bs), 0x200);
266 type = le16_to_cpu(vd->tag.id);
267 if (type != 2) /* TAG_ID_AVDP */
270 /* get desriptor list address and block count */
271 count = le32_to_cpu(vd->type.anchor.length) / bs;
272 loc = le32_to_cpu(vd->type.anchor.location);
274 /* pick the primary descriptor from the list */
275 for (b = 0; b < count; b++) {
276 vd = (struct volume_descriptor *) get_buffer(id->fd, off + ((loc + b) * bs), 0x200);
280 type = le16_to_cpu(vd->tag.id);
285 if (le32_to_cpu(vd->tag.location) != loc + b)
288 if (type == 1) /* TAG_ID_PVD */
294 clen = vd->type.primary.ident.clen;
296 set_label_string((guint8*)id->label, vd->type.primary.ident.c, 31);
298 set_unicode16((guint8*)id->label, sizeof(id->label), vd->type.primary.ident.c, BE, 31);
305 ghb_dvd_volname(const gchar *device)
308 gchar *buffer = NULL;
310 id.fd = open(device, O_RDONLY);
314 if (probe_udf (&id) == 0)
316 buffer = g_strdup(id.label);
323 ghb_resolve_symlink(const gchar *name)
329 gfile = g_file_new_for_path(name);
330 info = g_file_query_info(gfile,
331 G_FILE_ATTRIBUTE_STANDARD_NAME ","
332 G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET ","
333 G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK,
334 G_FILE_QUERY_INFO_NONE, NULL, NULL);
335 while ((info != NULL) && g_file_info_get_is_symlink(info))
340 parent = g_file_get_parent(gfile);
341 g_object_unref(gfile);
342 target = g_file_info_get_symlink_target(info);
343 gfile = g_file_resolve_relative_path(parent, target);
344 g_object_unref(parent);
346 g_object_unref(info);
347 info = g_file_query_info(gfile,
348 G_FILE_ATTRIBUTE_STANDARD_NAME ","
349 G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET ","
350 G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK,
351 G_FILE_QUERY_INFO_NONE, NULL, NULL);
355 file = g_file_get_path(gfile);
356 g_object_unref(info);
360 file = g_strdup(name);
362 g_object_unref(gfile);
367 ghb_dvd_set_current(const gchar *name, signal_user_data_t *ud)
372 gchar *resolved = ghb_resolve_symlink(name);
374 if (ud->current_dvd_device != NULL)
376 g_free(ud->current_dvd_device);
377 ud->current_dvd_device = NULL;
379 gfile = g_file_new_for_path(resolved);
380 info = g_file_query_info(gfile,
381 G_FILE_ATTRIBUTE_STANDARD_TYPE,
382 G_FILE_QUERY_INFO_NONE, NULL, NULL);
385 if (g_file_info_get_file_type(info) == G_FILE_TYPE_SPECIAL)
387 // I could go through the trouble to scan the connected drives and
388 // verify that this device is connected and is a DVD. But I don't
389 // think its neccessary.
390 ud->current_dvd_device = resolved;
392 g_object_unref(info);
398 g_object_unref(gfile);
403 if (ud->current_dvd_device != NULL)
405 g_free(ud->current_dvd_device);
406 ud->current_dvd_device = NULL;
408 g_strlcpy(drive, name, 4);
409 dtype = GetDriveType(drive);
410 if (dtype == DRIVE_CDROM)
412 ud->current_dvd_device = g_strdup(name);