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