xref: /illumos-gate/usr/src/cmd/cdrw/bstream.c (revision dc5a8425272d2602e4c21b95b9eeac2b897f45a1)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <fcntl.h>
30 #include <string.h>
31 #include <errno.h>
32 #include <sys/types.h>
33 #include <unistd.h>
34 #include <sys/stat.h>
35 #include <sys/statvfs.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <libintl.h>
39 #include <limits.h>
40 #include <audio/au.h>
41 
42 #include "bstream.h"
43 #include "util.h"
44 #include "audio.h"
45 #include "byteorder.h"
46 #include "main.h"
47 
48 int str_errno;
49 
50 char *
51 str_errno_to_string(int serrno)
52 {
53 	switch (serrno) {
54 	case STR_ERR_NO_ERR:
55 		return (gettext("No error"));
56 	case STR_ERR_NO_REG_FILE:
57 		return (gettext("Not a regular file"));
58 	case STR_ERR_NO_READ_STDIN:
59 		return (gettext("Stdin not open for reading"));
60 	case STR_ERR_AU_READ_ERR:
61 		return (gettext("Unable to read au header"));
62 	case STR_ERR_AU_UNSUPPORTED_FORMAT:
63 		return (gettext("Unsupported au format"));
64 	case STR_ERR_AU_BAD_HEADER:
65 		return (gettext("Bad au header"));
66 	case STR_ERR_WAV_READ_ERR:
67 		return (gettext("Unable to read wav header"));
68 	case STR_ERR_WAV_UNSUPPORTED_FORMAT:
69 		return (gettext("Unsupported wav format"));
70 	case STR_ERR_WAV_BAD_HEADER:
71 		return (gettext("Bad wav header"));
72 	default:
73 		return (gettext("unknown error"));
74 	}
75 }
76 
77 static int
78 file_stream_size(bstreamhandle h, off_t *size)
79 {
80 	struct stat st;
81 
82 	str_errno = 0;
83 
84 	if (fstat(h->bstr_fd, &st) < 0)
85 		return (0);
86 	if ((st.st_mode & S_IFMT) != S_IFREG) {
87 		str_errno = STR_ERR_NO_REG_FILE;
88 		return (0);
89 	}
90 	*size = st.st_size;
91 	return (1);
92 }
93 
94 static int
95 audio_stream_size(bstreamhandle h, off_t *size)
96 {
97 	str_errno = 0;
98 	*size = (off_t)(uintptr_t)(h->bstr_private);
99 	return (1);
100 }
101 
102 static int
103 file_stream_read(bstreamhandle h, uchar_t *buf, off_t size)
104 {
105 	str_errno = 0;
106 	return (read(h->bstr_fd, buf, size));
107 }
108 
109 static int
110 file_stream_write(bstreamhandle h, uchar_t *buf, off_t size)
111 {
112 	str_errno = 0;
113 	return (write(h->bstr_fd, buf, size));
114 }
115 
116 /*
117  * with reverse byteorder
118  */
119 static int
120 file_stream_read_wrbo(bstreamhandle h, uchar_t *buf, off_t size)
121 {
122 	int cnt;
123 
124 	str_errno = 0;
125 	cnt = read(h->bstr_fd, buf, size);
126 	if (cnt > 0) {
127 		int i;
128 		uchar_t ch;
129 
130 		for (i = 0; i < cnt; i += 2) {
131 			ch = buf[i];
132 			buf[i] = buf[i+1];
133 			buf[i+1] = ch;
134 		}
135 	}
136 	return (cnt);
137 }
138 
139 /*
140  * This will change the byteorder in the buffer but that is fine with us.
141  */
142 static int
143 file_stream_write_wrbo(bstreamhandle h, uchar_t *buf, off_t size)
144 {
145 	int i;
146 	uchar_t ch;
147 
148 	str_errno = 0;
149 	if (size > 0) {
150 		for (i = 0; i < size; i += 2) {
151 			ch = buf[i];
152 			buf[i] = buf[i+1];
153 			buf[i+1] = ch;
154 		}
155 	}
156 	return (write(h->bstr_fd, buf, size));
157 }
158 
159 static int
160 file_stream_close(bstreamhandle h)
161 {
162 	int fd;
163 
164 	str_errno = 0;
165 	fd = h->bstr_fd;
166 	free(h);
167 	return (close(fd));
168 }
169 
170 static int
171 stdin_stream_close(bstreamhandle h)
172 {
173 	str_errno = 0;
174 	free(h);
175 	return (0);
176 }
177 
178 static int
179 wav_write_stream_close(bstreamhandle h)
180 {
181 	uint32_t sz;
182 	Wave_filehdr wav;
183 
184 	str_errno = 0;
185 	(void) memset(&wav, 0, sizeof (wav));
186 	sz = lseek(h->bstr_fd, 0L, SEEK_END);
187 	(void) lseek(h->bstr_fd, 0L, SEEK_SET);
188 	if (read(h->bstr_fd, &wav, sizeof (wav)) != sizeof (wav)) {
189 		return (1);
190 	}
191 	wav.total_chunk_size = CPU_TO_LE32(sz - 8);
192 	wav.data_size = CPU_TO_LE32(sz - 44);
193 	(void) lseek(h->bstr_fd, 0L, SEEK_SET);
194 	if (write(h->bstr_fd, &wav, sizeof (wav)) != sizeof (wav)) {
195 		return (1);
196 	}
197 	(void) close(h->bstr_fd);
198 	free(h);
199 	return (0);
200 }
201 
202 static int
203 au_write_stream_close(bstreamhandle h)
204 {
205 	uint32_t sz;
206 
207 	str_errno = 0;
208 	sz = lseek(h->bstr_fd, 0L, SEEK_END);
209 	sz -= PRE_DEF_AU_HDR_LEN;
210 	sz = CPU_TO_BE32(sz);
211 	if (lseek(h->bstr_fd, 8L, SEEK_SET) < 0)
212 		return (1);
213 
214 	if (write(h->bstr_fd, &sz, 4) < 0)
215 		return (1);
216 
217 	(void) close(h->bstr_fd);
218 	free(h);
219 	return (0);
220 }
221 
222 /* ARGSUSED */
223 static void
224 stdin_stream_rewind(bstreamhandle h)
225 {
226 }
227 
228 static void
229 file_stream_rewind(bstreamhandle h)
230 {
231 	(void) lseek(h->bstr_fd, 0L, SEEK_SET);
232 }
233 
234 static void
235 au_stream_rewind(bstreamhandle h)
236 {
237 	au_filehdr_t au;
238 
239 	(void) lseek(h->bstr_fd, 0L, SEEK_SET);
240 	if (read(h->bstr_fd, &au, sizeof (au)) != sizeof (au)) {
241 		return;
242 	}
243 
244 	if (lseek(h->bstr_fd, (long)(BE32_TO_CPU(au.au_offset)),
245 	    SEEK_SET) < 0) {
246 		return;
247 	}
248 }
249 
250 static void
251 wav_stream_rewind(bstreamhandle h)
252 {
253 	(void) lseek(h->bstr_fd, (long)(sizeof (Wave_filehdr)), SEEK_SET);
254 }
255 
256 bstreamhandle
257 open_file_read_stream(char *file)
258 {
259 	bstreamhandle h;
260 	int fd;
261 	struct stat st;
262 
263 	str_errno = 0;
264 	if (stat(file, &st) < 0)
265 		return (NULL);
266 	if ((st.st_mode & S_IFMT) == S_IFDIR) {
267 		str_errno = STR_ERR_NO_REG_FILE;
268 		return (NULL);
269 	}
270 	fd = open(file, O_RDONLY);
271 	if (fd < 0)
272 		return (NULL);
273 	h = (bstreamhandle)my_zalloc(sizeof (*h));
274 	h->bstr_fd = fd;
275 	h->bstr_read = file_stream_read;
276 	h->bstr_close = file_stream_close;
277 	h->bstr_size = file_stream_size;
278 	h->bstr_rewind = file_stream_rewind;
279 
280 	return (h);
281 }
282 
283 bstreamhandle
284 open_stdin_read_stream(void)
285 {
286 	bstreamhandle h;
287 	int mode;
288 
289 	str_errno = 0;
290 	if ((mode = fcntl(0, F_GETFD, NULL)) < 0) {
291 		str_errno = STR_ERR_NO_READ_STDIN;
292 		return (NULL);
293 	}
294 	mode &= 3;
295 	if ((mode != O_RDONLY) && (mode != O_RDWR)) {
296 		str_errno = STR_ERR_NO_READ_STDIN;
297 		return (NULL);
298 	}
299 	h = (bstreamhandle)my_zalloc(sizeof (*h));
300 	h->bstr_fd = 0;
301 	h->bstr_read = file_stream_read;
302 	h->bstr_close = stdin_stream_close;
303 	h->bstr_size = file_stream_size;
304 	h->bstr_rewind = stdin_stream_rewind;
305 
306 	return (h);
307 }
308 
309 bstreamhandle
310 open_au_read_stream(char *fname)
311 {
312 	bstreamhandle h;
313 	int fd, sav;
314 	au_filehdr_t *au;
315 	struct stat st;
316 	uint32_t data_size;
317 
318 	au = NULL;
319 	str_errno = 0;
320 	fd = open(fname, O_RDONLY);
321 	if (fd < 0)
322 		return (NULL);
323 
324 	if (fstat(fd, &st) < 0) {
325 		goto au_open_failed;
326 	}
327 	if ((st.st_mode & S_IFMT) != S_IFREG) {
328 		str_errno = STR_ERR_NO_REG_FILE;
329 		goto au_open_failed;
330 	}
331 	au = (au_filehdr_t *)my_zalloc(sizeof (*au));
332 	if (read(fd, au, sizeof (*au)) != sizeof (*au)) {
333 		str_errno = STR_ERR_AU_READ_ERR;
334 		goto au_open_failed;
335 	}
336 	au->au_magic = BE32_TO_CPU(au->au_magic);
337 	au->au_offset = BE32_TO_CPU(au->au_offset);
338 	au->au_data_size = BE32_TO_CPU(au->au_data_size);
339 	au->au_encoding = BE32_TO_CPU(au->au_encoding);
340 	au->au_sample_rate = BE32_TO_CPU(au->au_sample_rate);
341 	au->au_channels = BE32_TO_CPU(au->au_channels);
342 
343 	if (au->au_magic != AUDIO_AU_FILE_MAGIC) {
344 		str_errno = STR_ERR_AU_BAD_HEADER;
345 		goto au_open_failed;
346 	}
347 	if ((au->au_encoding != AUDIO_AU_ENCODING_LINEAR_16) ||
348 	    (au->au_sample_rate != 44100) || (au->au_channels != 2)) {
349 
350 		str_errno = STR_ERR_AU_UNSUPPORTED_FORMAT;
351 		goto au_open_failed;
352 	}
353 	if (au->au_data_size != AUDIO_AU_UNKNOWN_SIZE) {
354 		if ((au->au_offset + au->au_data_size) != st.st_size) {
355 			str_errno = STR_ERR_AU_BAD_HEADER;
356 			goto au_open_failed;
357 		}
358 		data_size = au->au_data_size;
359 	} else {
360 		data_size = st.st_size - au->au_offset;
361 	}
362 	if (data_size == 0) {
363 		str_errno = STR_ERR_AU_UNSUPPORTED_FORMAT;
364 		goto au_open_failed;
365 	}
366 	if (lseek(fd, au->au_offset, SEEK_SET) < 0) {
367 		goto au_open_failed;
368 	}
369 
370 	free(au);
371 	h = (bstreamhandle)my_zalloc(sizeof (*h));
372 	h->bstr_fd = fd;
373 	h->bstr_read = file_stream_read_wrbo;
374 	h->bstr_close = file_stream_close;
375 	h->bstr_size = audio_stream_size;
376 	h->bstr_rewind = au_stream_rewind;
377 	h->bstr_private = (void *)data_size;
378 
379 	return (h);
380 
381 au_open_failed:
382 	sav = errno;
383 	(void) close(fd);
384 	if (au != NULL)
385 		free(au);
386 	errno = sav;
387 	return (NULL);
388 }
389 
390 bstreamhandle
391 open_wav_read_stream(char *fname)
392 {
393 	bstreamhandle h;
394 	int fd, sav;
395 	Wave_filehdr *wav;
396 	struct stat st;
397 	uint32_t data_size;
398 
399 	wav = NULL;
400 	str_errno = 0;
401 	fd = open(fname, O_RDONLY);
402 	if (fd < 0)
403 		return (NULL);
404 
405 	if (fstat(fd, &st) < 0) {
406 		goto wav_open_failed;
407 	}
408 	if ((st.st_mode & S_IFMT) != S_IFREG) {
409 		str_errno = STR_ERR_NO_REG_FILE;
410 		goto wav_open_failed;
411 	}
412 	wav = (Wave_filehdr *)my_zalloc(sizeof (*wav));
413 	if (read(fd, wav, sizeof (*wav)) != sizeof (*wav)) {
414 		str_errno = STR_ERR_WAV_READ_ERR;
415 		goto wav_open_failed;
416 	}
417 	if ((strncmp(wav->riff, "RIFF", 4) != 0) ||
418 		(strncmp(wav->wave, "WAVE", 4) != 0)) {
419 		str_errno = STR_ERR_WAV_BAD_HEADER;
420 		goto wav_open_failed;
421 	}
422 	if (((CPU_TO_LE32(wav->total_chunk_size) + 8) != st.st_size) ||
423 	    (strncmp(wav->fmt, "fmt ", 4) != 0) ||
424 	    (CPU_TO_LE16(wav->fmt_tag) != 1) ||
425 	    (CPU_TO_LE16(wav->n_channels) != 2) ||
426 	    (CPU_TO_LE32(wav->sample_rate) != 44100) ||
427 	    (CPU_TO_LE16(wav->bits_per_sample) != 16) ||
428 	    (strncmp(wav->data, "data", 4) != 0) ||
429 	    ((CPU_TO_LE32(wav->data_size) + 44) != st.st_size)) {
430 
431 		str_errno = STR_ERR_WAV_UNSUPPORTED_FORMAT;
432 		goto wav_open_failed;
433 	}
434 	data_size = CPU_TO_LE32(wav->data_size);
435 	if (lseek(fd, sizeof (*wav), SEEK_SET) < 0) {
436 		goto wav_open_failed;
437 	}
438 
439 	free(wav);
440 	h = (bstreamhandle)my_zalloc(sizeof (*h));
441 	h->bstr_fd = fd;
442 	h->bstr_read = file_stream_read;
443 	h->bstr_close = file_stream_close;
444 	h->bstr_size = audio_stream_size;
445 	h->bstr_rewind = wav_stream_rewind;
446 	h->bstr_private = (void *)data_size;
447 
448 	return (h);
449 
450 wav_open_failed:
451 	sav = errno;
452 	(void) close(fd);
453 	if (wav != NULL)
454 		free(wav);
455 	errno = sav;
456 	return (NULL);
457 }
458 
459 bstreamhandle
460 open_aur_read_stream(char *fname)
461 {
462 	bstreamhandle h;
463 
464 	h = open_file_read_stream(fname);
465 	if (h != NULL) {
466 		h->bstr_read = file_stream_read_wrbo;
467 	}
468 	return (h);
469 }
470 
471 bstreamhandle
472 open_au_write_stream(char *fname)
473 {
474 	bstreamhandle h;
475 	int esav, fd;
476 	uchar_t head[] = PRE_DEF_AU_HDR;
477 
478 	str_errno = 0;
479 	fd = -1;
480 	/* O_RDWR because we need to read while closing */
481 	fd = open(fname, O_RDWR|O_CREAT|O_TRUNC, 0666);
482 	if (fd < 0)
483 		goto open_au_write_stream_failed;
484 	if (write(fd, head, PRE_DEF_AU_HDR_LEN) != PRE_DEF_AU_HDR_LEN) {
485 		goto open_au_write_stream_failed;
486 	}
487 	h = (bstreamhandle)my_zalloc(sizeof (*h));
488 	h->bstr_fd = fd;
489 	h->bstr_write = file_stream_write_wrbo;
490 	h->bstr_close = au_write_stream_close;
491 	return (h);
492 
493 open_au_write_stream_failed:
494 	esav = errno;
495 	if (fd != -1)
496 		(void) close(fd);
497 	errno = esav;
498 	return (NULL);
499 }
500 
501 bstreamhandle
502 open_wav_write_stream(char *fname)
503 {
504 	bstreamhandle h;
505 	int esav, fd;
506 	uchar_t head[] = PRE_DEF_WAV_HDR;
507 
508 	str_errno = 0;
509 	fd = -1;
510 	fd = open(fname, O_RDWR|O_CREAT|O_TRUNC, 0666);
511 	if (fd < 0)
512 		goto open_wav_write_stream_failed;
513 	if (write(fd, head, PRE_DEF_WAV_HDR_LEN) != PRE_DEF_WAV_HDR_LEN) {
514 		goto open_wav_write_stream_failed;
515 	}
516 	h = (bstreamhandle)my_zalloc(sizeof (*h));
517 	h->bstr_fd = fd;
518 	h->bstr_write = file_stream_write;
519 	h->bstr_close = wav_write_stream_close;
520 	return (h);
521 
522 open_wav_write_stream_failed:
523 	esav = errno;
524 	if (fd != -1)
525 		(void) close(fd);
526 	errno = esav;
527 	return (NULL);
528 }
529 
530 bstreamhandle
531 open_aur_write_stream(char *fname)
532 {
533 	bstreamhandle h;
534 	int fd;
535 
536 	str_errno = 0;
537 	fd = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666);
538 	if (fd < 0)
539 		return (NULL);
540 	h = (bstreamhandle)my_zalloc(sizeof (*h));
541 	h->bstr_fd = fd;
542 	h->bstr_write = file_stream_write_wrbo;
543 	h->bstr_close = file_stream_close;
544 	return (h);
545 }
546 
547 bstreamhandle
548 open_file_write_stream(char *fname)
549 {
550 	bstreamhandle h;
551 	int fd;
552 
553 	str_errno = 0;
554 	fd = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666);
555 	if (fd < 0)
556 		return (NULL);
557 	h = (bstreamhandle)my_zalloc(sizeof (*h));
558 	h->bstr_fd = fd;
559 	h->bstr_write = file_stream_write;
560 	h->bstr_close = file_stream_close;
561 	return (h);
562 }
563 
564 bstreamhandle
565 open_temp_file_stream(void)
566 {
567 	bstreamhandle h;
568 	char *t;
569 	int fd;
570 
571 	str_errno = 0;
572 
573 	t = (char *)get_tmp_name();
574 
575 	if (strlcat(t, "/cdXXXXXX", PATH_MAX) >= PATH_MAX)
576 		return (NULL);
577 
578 	fd = mkstemp(t);
579 
580 	if (debug)
581 		(void) printf("temp is: %s length: %d\n", t, strlen(t));
582 
583 	if (fd < 0)
584 		return (NULL);
585 	(void) unlink(t);
586 
587 	h = (bstreamhandle)my_zalloc(sizeof (*h));
588 	h->bstr_fd = fd;
589 	h->bstr_read = file_stream_read;
590 	h->bstr_write = file_stream_write;
591 	h->bstr_close = file_stream_close;
592 	h->bstr_size = file_stream_size;
593 	h->bstr_rewind = file_stream_rewind;
594 
595 	return (h);
596 }
597 
598 /*
599  * check_avail_temp_space returns 0 if there is adequate space
600  * in the temporary directory, or a non-zero error code if
601  * something goes wrong
602  */
603 int
604 check_avail_temp_space(size_t req_size)
605 {
606 	struct statvfs buf;
607 	size_t free_size = 0;
608 
609 	if (statvfs(get_tmp_name(), &buf) < 0) {
610 		return (errno);
611 	}
612 
613 	free_size = buf.f_bfree * buf.f_frsize;
614 
615 	if (free_size <= req_size)
616 		return (ENOMEM);
617 
618 	return (0);
619 }
620 
621 
622 char *
623 get_tmp_name(void)
624 {
625 	char *t;
626 	char *envptr;
627 
628 	t = (char *)my_zalloc(PATH_MAX);
629 
630 	/*
631 	 * generate temp directory path based on this order:
632 	 * user specified (-m option), temp env variable,
633 	 * and finally /tmp if nothing is found.
634 	 */
635 
636 	if (alt_tmp_dir) {
637 
638 		/* copy and leave room for temp filename */
639 
640 		(void) strlcpy(t, alt_tmp_dir, PATH_MAX - 10);
641 	} else {
642 		envptr = getenv("TMPDIR");
643 		if (envptr != NULL) {
644 			(void) strlcpy(t, envptr, PATH_MAX - 10);
645 		} else {
646 			(void) strlcpy(t, "/tmp", 5);
647 		}
648 	}
649 
650 	/*
651 	 * no need to check if path is valid. statvfs will catch
652 	 * it later and fail with a proper error message.
653 	 */
654 
655 	return (t);
656 }
657