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 2004 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 <stdlib.h> 30 #include <stdio.h> 31 #include <unistd.h> 32 #include <errno.h> 33 #include <malloc.h> 34 #include <string.h> 35 #include <fcntl.h> 36 #include <sys/stat.h> 37 #include <sys/types.h> 38 #include <sys/mman.h> 39 40 #include <AudioFile.h> 41 #include <AudioLib.h> 42 #include <AudioDebug.h> 43 #include <libaudio.h> 44 #include <audio_hdr.h> 45 46 // XX64 This should go away when <sys/mman.h> gets fixed. 47 extern "C" int madvise(caddr_t, size_t, int); 48 49 // class AudioFile methods 50 51 52 // Initialize temporary file params 53 #define TMPDIR "/tmp" 54 #define TMPFILE "/audiotoolXXXXXX" 55 static char *tmpdir = NULL; 56 static const char *tmpname = "(temporary file)"; 57 static const FileAccess tmpmode = ReadWrite; 58 static const VMAccess defaccess = SequentialAccess; 59 60 // Initialize default access mode, used when a filename is supplied 61 const FileAccess AudioFile::defmode = ReadOnly; 62 63 // Default audio file path prefix environment variable 64 const char *AudioFile::AUDIO_PATH = "AUDIOPATH"; 65 66 67 // Constructor with no arguments opens a read/write temporary file 68 AudioFile:: 69 AudioFile(): 70 AudioUnixfile(tmpname, tmpmode), 71 hdrsize(0), seekpos(0), origlen(0.), mapaddr(0), maplen(0), 72 vmaccess(defaccess) 73 { 74 } 75 76 // Constructor with pathname and optional mode arg 77 AudioFile:: 78 AudioFile( 79 const char *path, // filename 80 const FileAccess acc): // access mode 81 AudioUnixfile(path, acc), 82 hdrsize(0), seekpos(0), origlen(0.), mapaddr(0), maplen(0), 83 vmaccess(defaccess) 84 { 85 } 86 87 // Destructor must call the local Close() routine 88 AudioFile:: 89 ~AudioFile() 90 { 91 // If the file was open, close it 92 if (opened()) 93 (void) Close(); 94 } 95 96 // Set a default temporary file directory 97 AudioError AudioFile:: 98 SetTempPath( 99 const char *path) 100 { 101 struct stat st; 102 103 // Verify intended path 104 if ((stat(path, &st) < 0) || 105 !S_ISDIR(st.st_mode) || 106 (access(path, W_OK) < 0)) { 107 errno = ENOTDIR; 108 return (AUDIO_UNIXERROR); 109 } 110 111 if (tmpdir != NULL) 112 (void) free(tmpdir); 113 tmpdir = (char *)malloc(strlen(path) + 1); 114 (void) strcpy(tmpdir, path); 115 return (AUDIO_SUCCESS); 116 } 117 118 119 // Create a named file according to the current mode setting 120 AudioError AudioFile:: 121 createfile( 122 const char *path) // pathname or 0 123 { 124 char *tmpf; 125 char *tmpstr; 126 int openmode; 127 int desc; 128 AudioError err; 129 130 // Convert the open mode to an int argument for open() 131 openmode = GetAccess(); 132 133 // Was the header properly set? 134 if (!hdrset()) 135 return (RaiseError(AUDIO_ERR_BADHDR)); 136 137 // Can't create if already opened or if mode or name not set 138 if ((openmode == -1) || opened() || (strlen(path) == 0)) 139 return (RaiseError(AUDIO_ERR_NOEFFECT)); 140 141 // If temporary file, create and unlink it. 142 if (strcmp(path, tmpname) == 0) { 143 // Construct the temporary file path 144 tmpstr = (char *)malloc(1 + strlen(TMPFILE) + 145 strlen((tmpdir == NULL) ? TMPDIR : tmpdir)); 146 (void) sprintf(tmpstr, "%s%s", 147 (tmpdir == NULL) ? TMPDIR : tmpdir, TMPFILE); 148 tmpf = mktemp(tmpstr); 149 150 // Open the temp file and unlink it 151 err = createfile(tmpf); 152 if ((err == AUDIO_SUCCESS) && (unlink(tmpf) < 0)) { 153 (void) Close(); 154 err = RaiseError(AUDIO_UNIXERROR, Warning); 155 } 156 (void) free(tmpstr); 157 return (err); 158 } 159 160 // Create the file 161 desc = open(path, openmode | O_CREAT | O_TRUNC, 0666); 162 if ((desc < 0) && (errno == EOVERFLOW)) { 163 return (RaiseError(AUDIO_UNIXERROR, Fatal, 164 (char *)"Large File")); 165 } else if (desc < 0) { 166 return (RaiseError(AUDIO_UNIXERROR)); 167 } 168 169 // Set the file descriptor (this marks the file open) 170 setfd(desc); 171 172 // Write the file header with current (usually unknown) size 173 err = encode_filehdr(); 174 if (err != AUDIO_SUCCESS) { 175 setfd(-1); 176 (void) close(desc); // If error, remove file 177 (void) unlink(path); 178 return (err); 179 } 180 181 // Save the length that got written, then set it to zero 182 origlen = GetLength(); 183 setlength(0.); 184 185 // Set the size of the file header 186 hdrsize = lseek(desc, (off_t)0, SEEK_CUR); 187 if (hdrsize < 0) { 188 setfd(-1); 189 (void) close(desc); // If error, remove file 190 (void) unlink(path); 191 return (err); 192 } 193 seekpos = 0; 194 195 return (AUDIO_SUCCESS); 196 } 197 198 // Create a file whose name is already set, according to the mode setting 199 AudioError AudioFile:: 200 Create() 201 { 202 return (createfile(GetName())); 203 } 204 205 // Open a file whose name is set 206 AudioError AudioFile:: 207 Open() 208 { 209 return (OpenPath(NULL)); 210 } 211 212 // Open a file, using the specified path prefixes 213 AudioError AudioFile:: 214 OpenPath( 215 const char *path) 216 { 217 char *filename; 218 int flen; 219 char *prefix; 220 char *str; 221 char *wrk; 222 char *pathname; 223 int openmode; 224 AudioError err; 225 226 // Convert the open mode to an int argument for open() 227 openmode = GetAccess(); 228 filename = GetName(); 229 flen = strlen(filename); 230 231 // Can't open if already opened or if mode or name not set 232 if ((openmode == -1) || opened() || (strlen(filename) == 0)) 233 return (RaiseError(AUDIO_ERR_NOEFFECT)); 234 235 // Search path: 236 // 1) try name: if not found and not readonly: 237 // if Append mode, try creating it 238 // 2) if name is a relative pathname, and 'path' is not NULL: 239 // try every path prefix in 'path' 240 241 err = tryopen(filename, openmode); 242 if (!err) 243 return (AUDIO_SUCCESS); 244 if (GetAccess().Writeable() || (filename[0] == '/')) { 245 // If file is non-existent and Append mode, try creating it. 246 if ((err == AUDIO_UNIXERROR) && (err.sys == ENOENT) && 247 GetAccess().Append() && hdrset()) { 248 return (Create()); 249 } 250 return (RaiseError(err)); 251 } 252 253 // Try path as an environment variable name, else assume it is a path 254 str = (path == NULL) ? NULL : getenv(path); 255 if (str == NULL) 256 str = (char *)path; 257 258 if (str != NULL) { 259 // Make a copy of the path, to parse it 260 wrk = new char[strlen(str) + 1]; 261 (void) strcpy(wrk, str); 262 str = wrk; 263 264 // Try each component as a path prefix 265 for (prefix = str; 266 (prefix != NULL) && (prefix[0] != '\0'); 267 prefix = str) { 268 str = strchr(str, ':'); 269 if (str != NULL) 270 *str++ = '\0'; 271 pathname = new char[strlen(prefix) + flen + 2]; 272 (void) sprintf(pathname, "%s/%s", prefix, filename); 273 err = tryopen(pathname, openmode); 274 delete pathname; 275 switch (err) { 276 case AUDIO_SUCCESS: // found the file 277 delete wrk; 278 return (RaiseError(err)); 279 // XXX - if file found but not audio, stop looking?? 280 } 281 } 282 delete wrk; 283 } 284 // Can't find file. Return the original error condition. 285 return (RaiseError(tryopen(filename, openmode))); 286 } 287 288 // Attempt to open the given audio file 289 AudioError AudioFile:: 290 tryopen( 291 const char *pathname, 292 int openmode) 293 { 294 struct stat st; 295 int desc; 296 AudioError err; 297 298 // If the name is changing, set the new one 299 if (pathname != GetName()) 300 SetName(pathname); 301 302 // Does the file exist? 303 if (stat(pathname, &st) < 0) 304 return (AUDIO_UNIXERROR); 305 306 // If not a regular file, stop right there 307 if (!S_ISREG(st.st_mode)) 308 return (AUDIO_ERR_BADFILEHDR); 309 310 // Open the file and check that it's an audio file 311 desc = open(GetName(), openmode); 312 if ((desc < 0) && (errno == EOVERFLOW)) { 313 return (RaiseError(AUDIO_UNIXERROR, Fatal, 314 (char *)"Large File")); 315 } else if (desc < 0) { 316 return (AUDIO_UNIXERROR); 317 } 318 319 // Set the file descriptor (this marks the file open) 320 setfd(desc); 321 322 err = decode_filehdr(); 323 if (err != AUDIO_SUCCESS) { 324 (void) close(desc); 325 setfd(-1); 326 return (err); 327 } 328 329 // Save the length of the data and the size of the file header 330 origlen = GetLength(); 331 hdrsize = (off_t)lseek(desc, (off_t)0, SEEK_CUR); 332 if (hdrsize < 0) { 333 (void) close(desc); 334 setfd(-1); 335 return (err); 336 } 337 seekpos = 0; 338 339 // If this is ReadOnly file, mmap() it. Don't worry if mmap() fails. 340 if (!GetAccess().Writeable()) { 341 maplen = st.st_size; 342 343 /* 344 * Can't mmap LITTLE_ENDIAN as they are converted in 345 * place. 346 */ 347 if (localByteOrder() == BIG_ENDIAN) { 348 if ((mapaddr = (caddr_t)mmap(0, (int)maplen, PROT_READ, 349 MAP_SHARED, desc, 0)) != (caddr_t)-1) { 350 // set default access method 351 (void) madvise(mapaddr, (unsigned int)maplen, 352 (int)GetAccessType()); 353 } else { 354 (void) RaiseError(AUDIO_UNIXERROR, Warning, 355 (char *)"Could not mmap() file"); 356 mapaddr = 0; 357 maplen = 0; 358 } 359 } else { 360 mapaddr = 0; 361 maplen = 0; 362 } 363 } 364 return (AUDIO_SUCCESS); 365 } 366 367 // set VM access hint for mmapped files 368 AudioError AudioFile:: 369 SetAccessType(VMAccess vmacc) 370 { 371 if (!opened()) { 372 return (AUDIO_ERR_NOEFFECT); 373 } 374 375 if (mapaddr == 0) { 376 return (AUDIO_ERR_NOEFFECT); 377 } 378 379 (void) madvise(mapaddr, (unsigned int)maplen, (int)vmacc); 380 vmaccess = vmacc; 381 382 return (AUDIO_SUCCESS); 383 } 384 385 // Close the file 386 AudioError AudioFile:: 387 Close() 388 { 389 AudioError err; 390 391 if (!opened()) 392 return (RaiseError(AUDIO_ERR_NOEFFECT, Warning)); 393 394 // Rewind the file and rewrite the header with the correct length 395 if (GetAccess().Writeable() && (origlen != GetLength())) { 396 397 // sanity check 398 if (GetHeader().Time_to_Bytes(GetLength()) != 399 (lseek(getfd(), (off_t)0, SEEK_END) - hdrsize)) { 400 PrintMsg(_MGET_( 401 "AudioFile:Close()...inconsistent length\n"), 402 Fatal); 403 } 404 405 // XXX - should be rewritten in C++ 406 err = (AudioError) audio_rewrite_filesize(getfd(), FILE_AU, 407 (uint_t)GetHeader().Time_to_Bytes(GetLength()), 0, 0); 408 } 409 410 // Call the generic file close routine 411 err = AudioUnixfile::Close(); 412 413 if (mapaddr) { 414 munmap(mapaddr, (int)maplen); 415 mapaddr = 0; 416 maplen = 0; 417 } 418 419 // Init important values, in case the file is reopened 420 hdrsize = 0; 421 seekpos = 0; 422 return (RaiseError(err)); 423 } 424 425 // Read data from underlying file into specified buffer. 426 // No data format translation takes place. 427 // The object's read position pointer is unaffected. 428 AudioError AudioFile:: 429 ReadData( 430 void* buf, // destination buffer address 431 size_t& len, // buffer length (updated) 432 Double& pos) // start position (updated) 433 { 434 off_t offset; 435 size_t cnt; 436 caddr_t cp; 437 AudioError err; 438 439 // If the file is not mapped, call parent ReadData() and return 440 if (mapaddr == 0) { 441 // Call the real routine 442 err = AudioUnixfile::ReadData(buf, len, pos); 443 // Update the cached seek pointer 444 seekpos += len; 445 return (err); 446 } 447 448 // If the file is mmapped, do a memcpy() from the mapaddr 449 450 // Save buffer size and zero transfer count 451 cnt = (size_t)len; 452 len = 0; 453 454 // Cannot read if file is not open 455 if (!opened() || !GetAccess().Readable()) 456 return (RaiseError(AUDIO_ERR_NOEFFECT)); 457 458 // Position must be valid 459 if (Undefined(pos) || (pos < 0.) || ((int)cnt < 0)) 460 return (RaiseError(AUDIO_ERR_BADARG)); 461 462 // Make sure we don't read off the end of file 463 offset = GetHeader().Time_to_Bytes(pos); 464 465 if ((offset + hdrsize) >= maplen) { 466 // trying to read past EOF 467 err = AUDIO_EOF; 468 err.sys = AUDIO_COPY_INPUT_EOF; 469 return (err); 470 } else if ((offset + hdrsize + cnt) > maplen) { 471 // re-adjust cnt so it reads up to the end of file 472 cnt = (size_t)(maplen - (offset + hdrsize)); 473 } 474 475 // Zero-length reads are finished 476 if (GetHeader().Bytes_to_Bytes(cnt) == 0) { 477 err = AUDIO_SUCCESS; 478 err.sys = AUDIO_COPY_ZERO_LIMIT; 479 return (err); 480 } else { 481 cp = mapaddr + offset + hdrsize; 482 memcpy((void*)buf, (void*)cp, cnt); 483 } 484 485 // Return the updated byte count and position 486 len = cnt; 487 pos = GetHeader().Bytes_to_Time(offset + len); 488 489 // Check to see if the endian is right. Note that special care 490 // doesn't need to be taken because of the mmap, since the data 491 // is copied into a separate buffer anyway. 492 coerceEndian((unsigned char *)buf, len, localByteOrder()); 493 494 return (AUDIO_SUCCESS); 495 } 496 497 // Write data to underlying file from specified buffer. 498 // No data format translation takes place. 499 // The object's write position pointer is unaffected. 500 AudioError AudioFile:: 501 WriteData( 502 void* buf, // source buffer address 503 size_t& len, // buffer length (updated) 504 Double& pos) // start position (updated) 505 { 506 AudioError err; 507 508 // Call the real routine 509 err = AudioUnixfile::WriteData(buf, len, pos); 510 511 // Update the cached seek pointer 512 seekpos += len; 513 return (err); 514 } 515 516 // Set the Unix file pointer to match a given file position. 517 AudioError AudioFile:: 518 seekread( 519 Double pos, // position to seek to 520 off_t& offset) // returned byte offset 521 { 522 offset = GetHeader().Time_to_Bytes(pos); 523 if (offset != seekpos) { 524 if (lseek(getfd(), (off_t)(hdrsize + offset), SEEK_SET) < 0) 525 return (RaiseError(AUDIO_UNIXERROR, Warning)); 526 seekpos = offset; 527 } 528 return (AUDIO_SUCCESS); 529 } 530 531 // Set the Unix file pointer to match a given file position. 532 // If seek beyond end-of-file, NULL out intervening data. 533 AudioError AudioFile:: 534 seekwrite( 535 Double pos, // position to seek to 536 off_t& offset) // returned byte offset 537 { 538 // If append-only, can't seek backwards into file 539 if (GetAccess().Append() && (pos < GetLength())) 540 return (RaiseError(AUDIO_ERR_NOEFFECT, Warning)); 541 542 // If seek beyond eof, fill data 543 if (pos > GetLength()) { 544 seekwrite(GetLength(), offset); // seek to eof 545 546 // XXX - not implemented yet 547 548 return (AUDIO_SUCCESS); 549 } 550 551 offset = GetHeader().Time_to_Bytes(pos); 552 if (offset != seekpos) { 553 if (lseek(getfd(), (off_t)(hdrsize + offset), SEEK_SET) < 0) 554 return (RaiseError(AUDIO_UNIXERROR, Warning)); 555 seekpos = offset; 556 } 557 return (AUDIO_SUCCESS); 558 } 559 560 // Copy routine that handles mapped files 561 AudioError AudioFile:: 562 AsyncCopy( 563 Audio* to, // audio object to copy to 564 Double& frompos, 565 Double& topos, 566 Double& limit) 567 { 568 caddr_t bptr; 569 size_t offset; 570 size_t cnt; 571 size_t svlim; 572 Double svfrom; 573 Double svto; 574 AudioHdr tohdr; 575 AudioError err; 576 577 // If this is NOT mmapped, or the destination is an AudioBuffer, 578 // use the default routine 579 if ((mapaddr == 0) || to->isBuffer()) { 580 return (Audio::AsyncCopy(to, frompos, topos, limit)); 581 } 582 583 tohdr = to->GetHeader(); 584 if (err = tohdr.Validate()) 585 return (err); 586 if (limit < 0.) 587 return (RaiseError(AUDIO_ERR_BADARG)); 588 svlim = (size_t)tohdr.Time_to_Bytes(limit); 589 590 // Get maximum possible copy length 591 svfrom = GetLength(); 592 if ((frompos >= svfrom) || ((cnt = (size_t) 593 GetHeader().Time_to_Bytes(svfrom - frompos)) == 0)) { 594 limit = 0.; 595 err = AUDIO_EOF; 596 err.sys = AUDIO_COPY_INPUT_EOF; 597 return (err); 598 } 599 if (!Undefined(limit) && (svlim < cnt)) 600 cnt = svlim; 601 602 limit = 0.; 603 604 offset = (size_t)GetHeader().Time_to_Bytes(frompos); 605 if ((offset + hdrsize) >= maplen) { 606 // trying to read past EOF 607 err = AUDIO_EOF; 608 err.sys = AUDIO_COPY_INPUT_EOF; 609 return (err); 610 } else if ((offset + hdrsize + cnt) > maplen) { 611 // re-adjust cnt so it reads up to the end of file 612 cnt = (size_t)(maplen - (offset + hdrsize)); 613 } 614 615 // Zero-length reads are done 616 if (GetHeader().Bytes_to_Bytes(cnt) == 0) { 617 err = AUDIO_SUCCESS; 618 err.sys = AUDIO_COPY_ZERO_LIMIT; 619 return (err); 620 } 621 622 // Write the data to the destination and update pointers/ctrs 623 svfrom = frompos; 624 svto = topos; 625 svlim = cnt; 626 bptr = mapaddr + hdrsize + offset; 627 err = to->WriteData(bptr, cnt, topos); 628 limit = topos - svto; 629 frompos = svfrom + limit; 630 631 // Report short writes 632 if (!err && (cnt < svlim)) 633 err.sys = AUDIO_COPY_SHORT_OUTPUT; 634 return (err); 635 } 636