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.
51 #define PACKED __attribute__((packed))
54 struct volume_descriptor {
55 struct descriptor_tag {
66 struct anchor_descriptor {
70 struct primary_descriptor {
81 struct volume_structure_descriptor {
87 #define VOLUME_ID_LABEL_SIZE 64
91 gchar label[VOLUME_ID_LABEL_SIZE+1];
100 #if (__BYTE_ORDER == __LITTLE_ENDIAN)
101 #define le16_to_cpu(x) (x)
102 #define le32_to_cpu(x) (x)
103 #define le64_to_cpu(x) (x)
104 #define be16_to_cpu(x) bswap_16(x)
105 #define be32_to_cpu(x) bswap_32(x)
106 #define cpu_to_le16(x) (x)
107 #define cpu_to_le32(x) (x)
108 #define cpu_to_be32(x) bswap_32(x)
109 #elif (__BYTE_ORDER == __BIG_ENDIAN)
110 #define le16_to_cpu(x) bswap_16(x)
111 #define le32_to_cpu(x) bswap_32(x)
112 #define le64_to_cpu(x) bswap_64(x)
113 #define be16_to_cpu(x) (x)
114 #define be32_to_cpu(x) (x)
115 #define cpu_to_le16(x) bswap_16(x)
116 #define cpu_to_le32(x) bswap_32(x)
117 #define cpu_to_be32(x) (x)
119 #endif /* __BYTE_ORDER */
121 #define UDF_VSD_OFFSET 0x8000
124 get_buffer(int fd, guint64 off, gsize len)
128 if (lseek(fd, off, SEEK_SET) < 0)
132 guint8 *buffer = g_malloc(len);
133 buf_len = read(fd, buffer, len);
143 set_unicode16(guint8 *str, gsize len, const guint8 *buf, gint endianess, gsize count)
149 for (ii = 0; ii + 2 <= count; ii += 2) {
151 c = (buf[ii+1] << 8) | buf[ii];
153 c = (buf[ii] << 8) | buf[ii+1];
157 } else if (c < 0x80) {
160 str[jj++] = (guint8) c;
161 } else if (c < 0x800) {
164 str[jj++] = (guint8) (0xc0 | (c >> 6));
165 str[jj++] = (guint8) (0x80 | (c & 0x3f));
169 str[jj++] = (guint8) (0xe0 | (c >> 12));
170 str[jj++] = (guint8) (0x80 | ((c >> 6) & 0x3f));
171 str[jj++] = (guint8) (0x80 | (c & 0x3f));
179 set_label_string(guint8 *str, const guint8 *buf, gsize count)
183 memcpy(str, buf, count);
186 /* remove trailing whitespace */
187 ii = strlen((gchar*)str);
190 if (!g_ascii_isspace(str[ii]))
197 probe_udf(udf_info_t *id)
199 struct volume_descriptor *vd;
200 struct volume_structure_descriptor *vsd;
209 vsd = (struct volume_structure_descriptor *) get_buffer(id->fd, off + UDF_VSD_OFFSET, 0x200);
213 if (memcmp(vsd->id, "NSR02", 5) == 0)
215 if (memcmp(vsd->id, "NSR03", 5) == 0)
217 if (memcmp(vsd->id, "BEA01", 5) == 0)
219 if (memcmp(vsd->id, "BOOT2", 5) == 0)
221 if (memcmp(vsd->id, "CD001", 5) == 0)
223 if (memcmp(vsd->id, "CDW02", 5) == 0)
225 if (memcmp(vsd->id, "TEA03", 5) == 0)
230 /* search the next VSD to get the logical block size of the volume */
231 for (bs = 0x800; bs < 0x8000; bs += 0x800) {
232 vsd = (struct volume_structure_descriptor *) get_buffer(id->fd, off + UDF_VSD_OFFSET + bs, 0x800);
235 if (vsd->id[0] != '\0')
241 /* search the list of VSDs for a NSR descriptor */
242 for (b = 0; b < 64; b++) {
243 vsd = (struct volume_structure_descriptor *) get_buffer(id->fd, off + UDF_VSD_OFFSET + (b * bs), 0x800);
247 if (vsd->id[0] == '\0')
249 if (memcmp(vsd->id, "NSR02", 5) == 0)
251 if (memcmp(vsd->id, "NSR03", 5) == 0)
257 /* read anchor volume descriptor */
258 vd = (struct volume_descriptor *) get_buffer(id->fd, off + (256 * bs), 0x200);
262 type = le16_to_cpu(vd->tag.id);
263 if (type != 2) /* TAG_ID_AVDP */
266 /* get desriptor list address and block count */
267 count = le32_to_cpu(vd->type.anchor.length) / bs;
268 loc = le32_to_cpu(vd->type.anchor.location);
270 /* pick the primary descriptor from the list */
271 for (b = 0; b < count; b++) {
272 vd = (struct volume_descriptor *) get_buffer(id->fd, off + ((loc + b) * bs), 0x200);
276 type = le16_to_cpu(vd->tag.id);
281 if (le32_to_cpu(vd->tag.location) != loc + b)
284 if (type == 1) /* TAG_ID_PVD */
290 clen = vd->type.primary.ident.clen;
292 set_label_string((guint8*)id->label, vd->type.primary.ident.c, 31);
294 set_unicode16((guint8*)id->label, sizeof(id->label), vd->type.primary.ident.c, BE, 31);
301 ghb_dvd_volname(const gchar *device)
304 gchar *buffer = NULL;
306 id.fd = open(device, O_RDONLY);
310 if (probe_udf (&id) == 0)
312 buffer = g_strdup(id.label);
318 #if defined(__linux__)
320 ghb_resolve_symlink(const gchar *name)
326 gfile = g_file_new_for_path(name);
327 info = g_file_query_info(gfile,
328 G_FILE_ATTRIBUTE_STANDARD_NAME ","
329 G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET ","
330 G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK,
331 G_FILE_QUERY_INFO_NONE, NULL, NULL);
332 while ((info != NULL) && g_file_info_get_is_symlink(info))
337 parent = g_file_get_parent(gfile);
338 g_object_unref(gfile);
339 target = g_file_info_get_symlink_target(info);
340 gfile = g_file_resolve_relative_path(parent, target);
341 g_object_unref(parent);
343 g_object_unref(info);
344 info = g_file_query_info(gfile,
345 G_FILE_ATTRIBUTE_STANDARD_NAME ","
346 G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET ","
347 G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK,
348 G_FILE_QUERY_INFO_NONE, NULL, NULL);
352 file = g_file_get_path(gfile);
353 g_object_unref(info);
357 file = g_strdup(name);
359 g_object_unref(gfile);
365 ghb_dvd_set_current(const gchar *name, signal_user_data_t *ud)
367 #if defined(__linux__)
370 gchar *resolved = ghb_resolve_symlink(name);
372 if (ud->current_dvd_device != NULL)
374 g_free(ud->current_dvd_device);
375 ud->current_dvd_device = NULL;
377 gfile = g_file_new_for_path(resolved);
378 info = g_file_query_info(gfile,
379 G_FILE_ATTRIBUTE_STANDARD_TYPE,
380 G_FILE_QUERY_INFO_NONE, NULL, NULL);
383 if (g_file_info_get_file_type(info) == G_FILE_TYPE_SPECIAL)
385 // I could go through the trouble to scan the connected drives and
386 // verify that this device is connected and is a DVD. But I don't
387 // think its neccessary.
388 ud->current_dvd_device = resolved;
390 g_object_unref(info);
396 g_object_unref(gfile);
398 if (ud->current_dvd_device != NULL)
400 g_free(ud->current_dvd_device);
401 ud->current_dvd_device = NULL;
403 ud->current_dvd_device = g_strdup(name);;