/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // XX64 This should go away when gets fixed. extern "C" int madvise(caddr_t, size_t, int); // class AudioFile methods // Initialize temporary file params #define TMPDIR "/tmp" #define TMPFILE "/audiotoolXXXXXX" static char *tmpdir = NULL; static const char *tmpname = "(temporary file)"; static const FileAccess tmpmode = ReadWrite; static const VMAccess defaccess = SequentialAccess; // Initialize default access mode, used when a filename is supplied const FileAccess AudioFile::defmode = ReadOnly; // Default audio file path prefix environment variable const char *AudioFile::AUDIO_PATH = "AUDIOPATH"; // Constructor with no arguments opens a read/write temporary file AudioFile:: AudioFile(): AudioUnixfile(tmpname, tmpmode), hdrsize(0), seekpos(0), origlen(0.), mapaddr(0), maplen(0), vmaccess(defaccess) { } // Constructor with pathname and optional mode arg AudioFile:: AudioFile( const char *path, // filename const FileAccess acc): // access mode AudioUnixfile(path, acc), hdrsize(0), seekpos(0), origlen(0.), mapaddr(0), maplen(0), vmaccess(defaccess) { } // Destructor must call the local Close() routine AudioFile:: ~AudioFile() { // If the file was open, close it if (opened()) (void) Close(); } // Set a default temporary file directory AudioError AudioFile:: SetTempPath( const char *path) { struct stat st; // Verify intended path if ((stat(path, &st) < 0) || !S_ISDIR(st.st_mode) || (access(path, W_OK) < 0)) { errno = ENOTDIR; return (AUDIO_UNIXERROR); } if (tmpdir != NULL) (void) free(tmpdir); tmpdir = (char *)malloc(strlen(path) + 1); (void) strcpy(tmpdir, path); return (AUDIO_SUCCESS); } // Create a named file according to the current mode setting AudioError AudioFile:: createfile( const char *path) // pathname or 0 { char *tmpf; char *tmpstr; int openmode; int desc; AudioError err; // Convert the open mode to an int argument for open() openmode = GetAccess(); // Was the header properly set? if (!hdrset()) return (RaiseError(AUDIO_ERR_BADHDR)); // Can't create if already opened or if mode or name not set if ((openmode == -1) || opened() || (strlen(path) == 0)) return (RaiseError(AUDIO_ERR_NOEFFECT)); // If temporary file, create and unlink it. if (strcmp(path, tmpname) == 0) { // Construct the temporary file path tmpstr = (char *)malloc(1 + strlen(TMPFILE) + strlen((tmpdir == NULL) ? TMPDIR : tmpdir)); (void) sprintf(tmpstr, "%s%s", (tmpdir == NULL) ? TMPDIR : tmpdir, TMPFILE); tmpf = mktemp(tmpstr); // Open the temp file and unlink it err = createfile(tmpf); if ((err == AUDIO_SUCCESS) && (unlink(tmpf) < 0)) { (void) Close(); err = RaiseError(AUDIO_UNIXERROR, Warning); } (void) free(tmpstr); return (err); } // Create the file desc = open(path, openmode | O_CREAT | O_TRUNC, 0666); if ((desc < 0) && (errno == EOVERFLOW)) { return (RaiseError(AUDIO_UNIXERROR, Fatal, (char *)"Large File")); } else if (desc < 0) { return (RaiseError(AUDIO_UNIXERROR)); } // Set the file descriptor (this marks the file open) setfd(desc); // Write the file header with current (usually unknown) size err = encode_filehdr(); if (err != AUDIO_SUCCESS) { setfd(-1); (void) close(desc); // If error, remove file (void) unlink(path); return (err); } // Save the length that got written, then set it to zero origlen = GetLength(); setlength(0.); // Set the size of the file header hdrsize = lseek(desc, (off_t)0, SEEK_CUR); if (hdrsize < 0) { setfd(-1); (void) close(desc); // If error, remove file (void) unlink(path); return (err); } seekpos = 0; return (AUDIO_SUCCESS); } // Create a file whose name is already set, according to the mode setting AudioError AudioFile:: Create() { return (createfile(GetName())); } // Open a file whose name is set AudioError AudioFile:: Open() { return (OpenPath(NULL)); } // Open a file, using the specified path prefixes AudioError AudioFile:: OpenPath( const char *path) { char *filename; int flen; char *prefix; char *str; char *wrk; char *pathname; int openmode; AudioError err; // Convert the open mode to an int argument for open() openmode = GetAccess(); filename = GetName(); flen = strlen(filename); // Can't open if already opened or if mode or name not set if ((openmode == -1) || opened() || (strlen(filename) == 0)) return (RaiseError(AUDIO_ERR_NOEFFECT)); // Search path: // 1) try name: if not found and not readonly: // if Append mode, try creating it // 2) if name is a relative pathname, and 'path' is not NULL: // try every path prefix in 'path' err = tryopen(filename, openmode); if (!err) return (AUDIO_SUCCESS); if (GetAccess().Writeable() || (filename[0] == '/')) { // If file is non-existent and Append mode, try creating it. if ((err == AUDIO_UNIXERROR) && (err.sys == ENOENT) && GetAccess().Append() && hdrset()) { return (Create()); } return (RaiseError(err)); } // Try path as an environment variable name, else assume it is a path str = (path == NULL) ? NULL : getenv(path); if (str == NULL) str = (char *)path; if (str != NULL) { // Make a copy of the path, to parse it wrk = new char[strlen(str) + 1]; (void) strcpy(wrk, str); str = wrk; // Try each component as a path prefix for (prefix = str; (prefix != NULL) && (prefix[0] != '\0'); prefix = str) { str = strchr(str, ':'); if (str != NULL) *str++ = '\0'; pathname = new char[strlen(prefix) + flen + 2]; (void) sprintf(pathname, "%s/%s", prefix, filename); err = tryopen(pathname, openmode); delete pathname; switch (err) { case AUDIO_SUCCESS: // found the file delete wrk; return (RaiseError(err)); // XXX - if file found but not audio, stop looking?? } } delete wrk; } // Can't find file. Return the original error condition. return (RaiseError(tryopen(filename, openmode))); } // Attempt to open the given audio file AudioError AudioFile:: tryopen( const char *pathname, int openmode) { struct stat st; int desc; AudioError err; // If the name is changing, set the new one if (pathname != GetName()) SetName(pathname); // Does the file exist? if (stat(pathname, &st) < 0) return (AUDIO_UNIXERROR); // If not a regular file, stop right there if (!S_ISREG(st.st_mode)) return (AUDIO_ERR_BADFILEHDR); // Open the file and check that it's an audio file desc = open(GetName(), openmode); if ((desc < 0) && (errno == EOVERFLOW)) { return (RaiseError(AUDIO_UNIXERROR, Fatal, (char *)"Large File")); } else if (desc < 0) { return (AUDIO_UNIXERROR); } // Set the file descriptor (this marks the file open) setfd(desc); err = decode_filehdr(); if (err != AUDIO_SUCCESS) { (void) close(desc); setfd(-1); return (err); } // Save the length of the data and the size of the file header origlen = GetLength(); hdrsize = (off_t)lseek(desc, (off_t)0, SEEK_CUR); if (hdrsize < 0) { (void) close(desc); setfd(-1); return (err); } seekpos = 0; // If this is ReadOnly file, mmap() it. Don't worry if mmap() fails. if (!GetAccess().Writeable()) { maplen = st.st_size; /* * Can't mmap LITTLE_ENDIAN as they are converted in * place. */ if (localByteOrder() == BIG_ENDIAN) { if ((mapaddr = (caddr_t)mmap(0, (int)maplen, PROT_READ, MAP_SHARED, desc, 0)) != (caddr_t)-1) { // set default access method (void) madvise(mapaddr, (unsigned int)maplen, (int)GetAccessType()); } else { (void) RaiseError(AUDIO_UNIXERROR, Warning, (char *)"Could not mmap() file"); mapaddr = 0; maplen = 0; } } else { mapaddr = 0; maplen = 0; } } return (AUDIO_SUCCESS); } // set VM access hint for mmapped files AudioError AudioFile:: SetAccessType(VMAccess vmacc) { if (!opened()) { return (AUDIO_ERR_NOEFFECT); } if (mapaddr == 0) { return (AUDIO_ERR_NOEFFECT); } (void) madvise(mapaddr, (unsigned int)maplen, (int)vmacc); vmaccess = vmacc; return (AUDIO_SUCCESS); } // Close the file AudioError AudioFile:: Close() { AudioError err; if (!opened()) return (RaiseError(AUDIO_ERR_NOEFFECT, Warning)); // Rewind the file and rewrite the header with the correct length if (GetAccess().Writeable() && (origlen != GetLength())) { // sanity check if (GetHeader().Time_to_Bytes(GetLength()) != (lseek(getfd(), (off_t)0, SEEK_END) - hdrsize)) { PrintMsg(_MGET_( "AudioFile:Close()...inconsistent length\n"), Fatal); } // XXX - should be rewritten in C++ err = (AudioError) audio_rewrite_filesize(getfd(), FILE_AU, (uint_t)GetHeader().Time_to_Bytes(GetLength()), 0, 0); } // Call the generic file close routine err = AudioUnixfile::Close(); if (mapaddr) { munmap(mapaddr, (int)maplen); mapaddr = 0; maplen = 0; } // Init important values, in case the file is reopened hdrsize = 0; seekpos = 0; return (RaiseError(err)); } // Read data from underlying file into specified buffer. // No data format translation takes place. // The object's read position pointer is unaffected. AudioError AudioFile:: ReadData( void* buf, // destination buffer address size_t& len, // buffer length (updated) Double& pos) // start position (updated) { off_t offset; size_t cnt; caddr_t cp; AudioError err; // If the file is not mapped, call parent ReadData() and return if (mapaddr == 0) { // Call the real routine err = AudioUnixfile::ReadData(buf, len, pos); // Update the cached seek pointer seekpos += len; return (err); } // If the file is mmapped, do a memcpy() from the mapaddr // Save buffer size and zero transfer count cnt = (size_t)len; len = 0; // Cannot read if file is not open if (!opened() || !GetAccess().Readable()) return (RaiseError(AUDIO_ERR_NOEFFECT)); // Position must be valid if (Undefined(pos) || (pos < 0.) || ((int)cnt < 0)) return (RaiseError(AUDIO_ERR_BADARG)); // Make sure we don't read off the end of file offset = GetHeader().Time_to_Bytes(pos); if ((offset + hdrsize) >= maplen) { // trying to read past EOF err = AUDIO_EOF; err.sys = AUDIO_COPY_INPUT_EOF; return (err); } else if ((offset + hdrsize + cnt) > maplen) { // re-adjust cnt so it reads up to the end of file cnt = (size_t)(maplen - (offset + hdrsize)); } // Zero-length reads are finished if (GetHeader().Bytes_to_Bytes(cnt) == 0) { err = AUDIO_SUCCESS; err.sys = AUDIO_COPY_ZERO_LIMIT; return (err); } else { cp = mapaddr + offset + hdrsize; memcpy((void*)buf, (void*)cp, cnt); } // Return the updated byte count and position len = cnt; pos = GetHeader().Bytes_to_Time(offset + len); // Check to see if the endian is right. Note that special care // doesn't need to be taken because of the mmap, since the data // is copied into a separate buffer anyway. coerceEndian((unsigned char *)buf, len, localByteOrder()); return (AUDIO_SUCCESS); } // Write data to underlying file from specified buffer. // No data format translation takes place. // The object's write position pointer is unaffected. AudioError AudioFile:: WriteData( void* buf, // source buffer address size_t& len, // buffer length (updated) Double& pos) // start position (updated) { AudioError err; // Call the real routine err = AudioUnixfile::WriteData(buf, len, pos); // Update the cached seek pointer seekpos += len; return (err); } // Set the Unix file pointer to match a given file position. AudioError AudioFile:: seekread( Double pos, // position to seek to off_t& offset) // returned byte offset { offset = GetHeader().Time_to_Bytes(pos); if (offset != seekpos) { if (lseek(getfd(), (off_t)(hdrsize + offset), SEEK_SET) < 0) return (RaiseError(AUDIO_UNIXERROR, Warning)); seekpos = offset; } return (AUDIO_SUCCESS); } // Set the Unix file pointer to match a given file position. // If seek beyond end-of-file, NULL out intervening data. AudioError AudioFile:: seekwrite( Double pos, // position to seek to off_t& offset) // returned byte offset { // If append-only, can't seek backwards into file if (GetAccess().Append() && (pos < GetLength())) return (RaiseError(AUDIO_ERR_NOEFFECT, Warning)); // If seek beyond eof, fill data if (pos > GetLength()) { seekwrite(GetLength(), offset); // seek to eof // XXX - not implemented yet return (AUDIO_SUCCESS); } offset = GetHeader().Time_to_Bytes(pos); if (offset != seekpos) { if (lseek(getfd(), (off_t)(hdrsize + offset), SEEK_SET) < 0) return (RaiseError(AUDIO_UNIXERROR, Warning)); seekpos = offset; } return (AUDIO_SUCCESS); } // Copy routine that handles mapped files AudioError AudioFile:: AsyncCopy( Audio* to, // audio object to copy to Double& frompos, Double& topos, Double& limit) { caddr_t bptr; size_t offset; size_t cnt; size_t svlim; Double svfrom; Double svto; AudioHdr tohdr; AudioError err; // If this is NOT mmapped, or the destination is an AudioBuffer, // use the default routine if ((mapaddr == 0) || to->isBuffer()) { return (Audio::AsyncCopy(to, frompos, topos, limit)); } tohdr = to->GetHeader(); if (err = tohdr.Validate()) return (err); if (limit < 0.) return (RaiseError(AUDIO_ERR_BADARG)); svlim = (size_t)tohdr.Time_to_Bytes(limit); // Get maximum possible copy length svfrom = GetLength(); if ((frompos >= svfrom) || ((cnt = (size_t) GetHeader().Time_to_Bytes(svfrom - frompos)) == 0)) { limit = 0.; err = AUDIO_EOF; err.sys = AUDIO_COPY_INPUT_EOF; return (err); } if (!Undefined(limit) && (svlim < cnt)) cnt = svlim; limit = 0.; offset = (size_t)GetHeader().Time_to_Bytes(frompos); if ((offset + hdrsize) >= maplen) { // trying to read past EOF err = AUDIO_EOF; err.sys = AUDIO_COPY_INPUT_EOF; return (err); } else if ((offset + hdrsize + cnt) > maplen) { // re-adjust cnt so it reads up to the end of file cnt = (size_t)(maplen - (offset + hdrsize)); } // Zero-length reads are done if (GetHeader().Bytes_to_Bytes(cnt) == 0) { err = AUDIO_SUCCESS; err.sys = AUDIO_COPY_ZERO_LIMIT; return (err); } // Write the data to the destination and update pointers/ctrs svfrom = frompos; svto = topos; svlim = cnt; bptr = mapaddr + hdrsize + offset; err = to->WriteData(bptr, cnt, topos); limit = topos - svto; frompos = svfrom + limit; // Report short writes if (!err && (cnt < svlim)) err.sys = AUDIO_COPY_SHORT_OUTPUT; return (err); }