int nxffs_rdblkhdr(FAR struct nxffs_volume_s *volume, off_t offset, FAR uint16_t *datlen) { struct nxffs_data_s blkhdr; uint32_t ecrc; uint32_t crc; uint16_t doffset; uint16_t dlen; int ret; /* Make sure that the block containing the data block header is in the cache */ nxffs_ioseek(volume, offset); ret = nxffs_rdcache(volume, volume->ioblock); if (ret < 0) { fdbg("ERROR: Failed to read data into cache: %d\n", ret); return ret; } /* Read the header at the FLASH offset */ doffset = volume->iooffset; memcpy(&blkhdr, &volume->cache[doffset], SIZEOF_NXFFS_DATA_HDR); /* Extract the data length */ dlen = nxffs_rdle16(blkhdr.datlen); /* Get the offset to the beginning of the data */ doffset += SIZEOF_NXFFS_DATA_HDR; /* Make sure that all of the data fits within the block */ if ((uint32_t)doffset + (uint32_t)dlen > (uint32_t)volume->geo.blocksize) { fdbg("ERROR: Data length=%d is unreasonable at offset=%d\n", dlen, doffset); return -EIO; } /* Extract the expected CRC and calculate the CRC of the data block */ ecrc = nxffs_rdle32(blkhdr.crc); nxffs_wrle32(blkhdr.crc, 0); crc = crc32((FAR const uint8_t *)&blkhdr, SIZEOF_NXFFS_DATA_HDR); crc = crc32part(&volume->cache[doffset], dlen, crc); if (crc != ecrc) { fdbg("ERROR: CRC failure\n"); return -EIO; } /* Looks good! Return the data length and success */ *datlen = dlen; return OK; }
static inline int nxffs_wrname(FAR struct nxffs_volume_s *volume, FAR struct nxffs_entry_s *entry, int namlen) { int ret; /* Seek to the inode name position and assure that it is in the volume * cache. */ nxffs_ioseek(volume, entry->noffset); ret = nxffs_rdcache(volume, volume->ioblock); if (ret < 0) { fdbg("ERROR: Failed to read inode name block %d: %d\n", volume->ioblock, -ret); return ret; } /* Copy the inode name to the volume cache and write the inode name block */ memcpy(&volume->cache[volume->iooffset], entry->name, namlen); ret = nxffs_wrcache(volume); if (ret < 0) { fdbg("ERROR: Failed to write inode header block %d: %d\n", volume->ioblock, -ret); } return ret; }
static ssize_t nxffs_rdseek(FAR struct nxffs_volume_s *volume, FAR struct nxffs_entry_s *entry, off_t fpos, FAR struct nxffs_blkentry_s *blkentry) { size_t datstart; size_t datend; off_t offset; int ret; /* The initial FLASH offset will be the offset to first data block of * the inode */ offset = entry->doffset; if (offset == 0) { /* Zero length files will have no data blocks */ return -ENOSPC; } /* Loop until we read the data block containing the desired position */ datend = 0; do { /* Check if the next data block contains the sought after file position */ ret = nxffs_nextblock(volume, offset, blkentry); if (ret < 0) { fdbg("ERROR: nxffs_nextblock failed: %d\n", -ret); return ret; } /* Get the range of data offsets for this data block */ datstart = datend; datend += blkentry->datlen; /* Offset to search for the next data block */ offset = blkentry->hoffset + SIZEOF_NXFFS_DATA_HDR + blkentry->datlen; } while (datend <= fpos); /* Return the offset to the data within the current data block */ blkentry->foffset = fpos - datstart; nxffs_ioseek(volume, blkentry->hoffset + SIZEOF_NXFFS_DATA_HDR + blkentry->foffset); return OK; }
int nxffs_wrinode(FAR struct nxffs_volume_s *volume, FAR struct nxffs_entry_s *entry) { FAR struct nxffs_inode_s *inode; uint32_t crc; int namlen; int ret; /* Seek to the inode header position and assure that it is in the volume * cache. */ nxffs_ioseek(volume, entry->hoffset); ret = nxffs_rdcache(volume, volume->ioblock); if (ret < 0) { fdbg("ERROR: Failed to read inode header block %d: %d\n", volume->ioblock, -ret); goto errout; } /* Get the length of the inode name */ namlen = strlen(entry->name); DEBUGASSERT(namlen < CONFIG_NXFFS_MAXNAMLEN); /* This was verified earlier */ /* Initialize the inode header */ inode = (FAR struct nxffs_inode_s *)&volume->cache[volume->iooffset]; memcpy(inode->magic, g_inodemagic, NXFFS_MAGICSIZE); inode->state = CONFIG_NXFFS_ERASEDSTATE; inode->namlen = namlen; nxffs_wrle32(inode->noffs, entry->noffset); nxffs_wrle32(inode->doffs, entry->doffset); nxffs_wrle32(inode->utc, entry->utc); nxffs_wrle32(inode->crc, 0); nxffs_wrle32(inode->datlen, entry->datlen); /* Calculate the CRC */ crc = crc32((FAR const uint8_t *)inode, SIZEOF_NXFFS_INODE_HDR); crc = crc32part((FAR const uint8_t *)entry->name, namlen, crc); /* Finish the inode header */ inode->state = INODE_STATE_FILE; nxffs_wrle32(inode->crc, crc); /* Write the block with the inode header */ ret = nxffs_wrcache(volume); if (ret < 0) { fdbg("ERROR: Failed to write inode header block %d: %d\n", volume->ioblock, -ret); } /* The volume is now available for other writers */ errout: sem_post(&volume->wrsem); return ret; }
int nxffs_rminode(FAR struct nxffs_volume_s *volume, FAR const char *name) { FAR struct nxffs_ofile_s *ofile; FAR struct nxffs_inode_s *inode; struct nxffs_entry_s entry; int ret; /* Check if the file is open */ ofile = nxffs_findofile(volume, name); if (ofile) { /* We can't remove the inode if it is open */ fdbg("Inode '%s' is open\n", name); ret = -EBUSY; goto errout; } /* Find the NXFFS inode */ ret = nxffs_findinode(volume, name, &entry); if (ret < 0) { fdbg("Inode '%s' not found\n", name); goto errout; } /* Set the position to the FLASH offset of the file header (nxffs_findinode * should have left the block in the cache). */ nxffs_ioseek(volume, entry.hoffset); /* Make sure that the block is in the cache */ ret = nxffs_rdcache(volume, volume->ioblock); if (ret < 0) { fdbg("Failed to read data into cache: %d\n", ret); goto errout_with_entry; } /* Change the file status... it is no longer valid */ inode = (FAR struct nxffs_inode_s *)&volume->cache[volume->iooffset]; inode->state = INODE_STATE_DELETED; /* Then write the cached block back to FLASH */ ret = nxffs_wrcache(volume); if (ret < 0) { fdbg("Failed to read data into cache: %d\n", ret); } errout_with_entry: nxffs_freeentry(&entry); errout: return ret; }
int nxffs_nextblock(FAR struct nxffs_volume_s *volume, off_t offset, FAR struct nxffs_blkentry_s *blkentry) { int nmagic; int ch; int nerased; int ret; /* Seek to the first FLASH offset provided by the caller. */ nxffs_ioseek(volume, offset); /* Skip the block header */ if (volume->iooffset < SIZEOF_NXFFS_BLOCK_HDR) { volume->iooffset = SIZEOF_NXFFS_BLOCK_HDR; } /* Then begin searching */ nerased = 0; nmagic = 0; for (;;) { /* Read the next character */ ch = nxffs_getc(volume, SIZEOF_NXFFS_DATA_HDR - nmagic); if (ch < 0) { fdbg("ERROR: nxffs_getc failed: %d\n", -ch); return ch; } /* Check for another erased byte */ else if (ch == CONFIG_NXFFS_ERASEDSTATE) { /* If we have encountered NXFFS_NERASED number of consecutive * erased bytes, then presume we have reached the end of valid * data. */ if (++nerased >= NXFFS_NERASED) { fvdbg("No entry found\n"); return -ENOENT; } } else { nerased = 0; /* Check for the magic sequence indicating the start of an NXFFS * data block or start of the next inode. There is the possibility * of this magic sequnce occurring in FLASH data. However, the * data block CRC should distinguish between real NXFFS data blocks * headers and such false alarms. */ if (ch != g_datamagic[nmagic]) { /* Ooops... this is the not the right character for the magic * Sequence. Check if we need to restart or to cancel the sequence: */ if (ch == g_datamagic[0]) { nmagic = 1; } else { nmagic = 0; } } else if (nmagic < NXFFS_MAGICSIZE - 1) { /* We have one more character in the magic sequence */ nmagic++; } /* We have found the magic sequence in the FLASH data that may * indicate the beginning of an NXFFS data block. */ else { /* The offset to the header must be 4 bytes before the current * FLASH seek location. */ blkentry->hoffset = nxffs_iotell(volume) - NXFFS_MAGICSIZE; /* Read the block header and verify the block at that address */ ret = nxffs_rdblkhdr(volume, blkentry->hoffset, &blkentry->datlen); if (ret == OK) { fvdbg("Found a valid data block, offset: %d datlen: %d\n", blkentry->hoffset, blkentry->datlen); return OK; } /* False alarm.. Restore the volume cache position (that was * destroyed by nxfs_rdblkhdr()) and keep looking. */ nxffs_ioseek(volume, blkentry->hoffset + NXFFS_MAGICSIZE); nmagic = 0; } } } /* We won't get here, but to keep some compilers happy: */ return -ENOENT; }
int nxffs_wrblkhdr(FAR struct nxffs_volume_s *volume, FAR struct nxffs_wrfile_s *wrfile) { FAR struct nxffs_data_s *dathdr; int ret; /* Write the data block header to memory */ nxffs_ioseek(volume, wrfile->doffset); dathdr = (FAR struct nxffs_data_s *)&volume->cache[volume->iooffset]; memcpy(dathdr->magic, g_datamagic, NXFFS_MAGICSIZE); nxffs_wrle32(dathdr->crc, 0); nxffs_wrle16(dathdr->datlen, wrfile->datlen); /* Update the entire data block CRC (including the header) */ wrfile->crc = crc32(&volume->cache[volume->iooffset], wrfile->datlen + SIZEOF_NXFFS_DATA_HDR); nxffs_wrle32(dathdr->crc, wrfile->crc); /* And write the data block to FLASH */ ret = nxffs_wrcache(volume); if (ret < 0) { fdbg("ERROR: nxffs_wrcache failed: %d\n", -ret); goto errout; } /* After the block has been successfully written to flash, update the inode * statistics and reset the write state. * * volume: * froffset - The offset the next free FLASH region. Set to just after * the inode data block that we just wrote. This is where we will * begin the search for the next inode header or data block. */ volume->froffset = (wrfile->doffset + wrfile->datlen + SIZEOF_NXFFS_DATA_HDR); /* wrfile->file.entry: * datlen: Total file length accumulated so far. When the file is * closed, this will hold the file length. * doffset: Offset to the first data block. Only the offset to the * first data block is saved. */ wrfile->ofile.entry.datlen += wrfile->datlen; if (wrfile->ofile.entry.doffset == 0) { wrfile->ofile.entry.doffset = wrfile->doffset; } /* Return success */ ret = OK; errout: wrfile->crc = 0; wrfile->doffset = 0; wrfile->datlen = 0; return ret; }
int nxffs_limits(FAR struct nxffs_volume_s *volume) { FAR struct nxffs_entry_s entry; off_t block; off_t offset; bool noinodes = false; int nerased; int ret; /* Get the offset to the first valid block on the FLASH */ block = 0; ret = nxffs_validblock(volume, &block); if (ret < 0) { fdbg("Failed to find a valid block: %d\n", -ret); return ret; } /* Then find the first valid inode in or beyond the first valid block */ offset = block * volume->geo.blocksize; ret = nxffs_nextentry(volume, offset, &entry); if (ret < 0) { /* The value -ENOENT is special. This simply means that the FLASH * was searched to the end and no valid inode was found... the file * system is empty (or, in more perverse cases, all inodes are * deleted or corrupted). */ if (ret != -ENOENT) { fdbg("nxffs_nextentry failed: %d\n", -ret); return ret; } /* Set a flag the just indicates that no inodes were found. Later, * we will set the location of the first inode to be the same as * the location of the free FLASH region. */ fvdbg("No inodes found\n"); noinodes = true; } else { /* Save the offset to the first inode */ volume->inoffset = entry.hoffset; fvdbg("First inode at offset %d\n", volume->inoffset); /* Discard this entry and set the next offset. */ offset = nxffs_inodeend(volume, &entry); nxffs_freeentry(&entry); } /* Now, search for the last valid entry */ if (!noinodes) { while ((ret = nxffs_nextentry(volume, offset, &entry)) == OK) { /* Discard the entry and guess the next offset. */ offset = nxffs_inodeend(volume, &entry); nxffs_freeentry(&entry); } fvdbg("Last inode before offset %d\n", offset); } /* No inodes were found after this offset. Now search for a block of * erased flash. */ nxffs_ioseek(volume, offset); nerased = 0; for (;;) { int ch = nxffs_getc(volume, 1); if (ch < 0) { /* Failed to read the next byte... this could mean that the FLASH * is full? */ if (volume->ioblock + 1 >= volume->nblocks && volume->iooffset + 1 >= volume->geo.blocksize) { /* Yes.. the FLASH is full. Force the offsets to the end of FLASH */ volume->froffset = volume->nblocks * volume->geo.blocksize; fvdbg("Assume no free FLASH, froffset: %d\n", volume->froffset); if (noinodes) { volume->inoffset = volume->froffset; fvdbg("No inodes, inoffset: %d\n", volume->inoffset); } return OK; } // No? Then it is some other failure that we do not know how to handle fdbg("nxffs_getc failed: %d\n", -ch); return ch; } /* Check for another erased byte */ else if (ch == CONFIG_NXFFS_ERASEDSTATE) { /* If we have encountered NXFFS_NERASED number of consecutive * erased bytes, then presume we have reached the end of valid * data. */ if (++nerased >= NXFFS_NERASED) { /* Okay.. we have a long stretch of erased FLASH in a valid * FLASH block. Let's say that this is the beginning of * the free FLASH region. */ volume->froffset = offset; fvdbg("Free FLASH region begins at offset: %d\n", volume->froffset); if (noinodes) { volume->inoffset = offset; fvdbg("First inode at offset %d\n", volume->inoffset); } return OK; } } else { offset += nerased + 1; nerased = 0; } } /* Won't get here */ return OK; }
int nxffs_wrreserve(FAR struct nxffs_volume_s *volume, size_t size) { int ret; /* Seek to the beginning of the free FLASH region */ nxffs_ioseek(volume, volume->froffset); /* Check for a seek past the end of the volume */ if (volume->ioblock >= volume->nblocks) { /* Return -ENOSPC to indicate that the volume is full */ return -ENOSPC; } /* Skip over block headers */ if (volume->iooffset < SIZEOF_NXFFS_BLOCK_HDR) { volume->iooffset = SIZEOF_NXFFS_BLOCK_HDR; } /* Make sure that there is space there to hold the entire object */ if (volume->iooffset + size > volume->geo.blocksize) { /* We will need to skip to the next block. But first, check if we are * already at the final block. */ if (volume->ioblock + 1 >= volume->nblocks) { /* Return -ENOSPC to indicate that the volume is full */ fdbg("ERROR: No space in last block\n"); return -ENOSPC; } /* This is not the last block in the volume, so just seek to the * beginning of the next, valid block. */ volume->ioblock++; ret = nxffs_validblock(volume, &volume->ioblock); if (ret < 0) { fdbg("ERROR: No more valid blocks\n"); return ret; } volume->iooffset = SIZEOF_NXFFS_BLOCK_HDR; } /* Update the pointer to the first next free FLASH memory -- reserving this * block of memory. */ volume->froffset = nxffs_iotell(volume) + size; return OK; }
ssize_t nxffs_write(FAR struct file *filep, FAR const char *buffer, size_t buflen) { FAR struct nxffs_volume_s *volume; FAR struct nxffs_wrfile_s *wrfile; ssize_t remaining; ssize_t nwritten; ssize_t total; int ret; fvdbg("Write %d bytes to offset %d\n", buflen, filep->f_pos); /* Sanity checks */ DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL); /* Recover the open file state from the struct file instance */ wrfile = (FAR struct nxffs_wrfile_s *)filep->f_priv; /* Recover the volume state from the open file */ volume = (FAR struct nxffs_volume_s *)filep->f_inode->i_private; DEBUGASSERT(volume != NULL); /* Get exclusive access to the volume. Note that the volume exclsem * protects the open file list. */ ret = sem_wait(&volume->exclsem); if (ret != OK) { ret = -errno; fdbg("ERROR: sem_wait failed: %d\n", ret); goto errout; } /* Check if the file was opened with write access */ if ((wrfile->ofile.oflags & O_WROK) == 0) { fdbg("ERROR: File not open for write access\n"); ret = -EACCES; goto errout_with_semaphore; } /* Loop until we successfully appended all of the data to the file (or an * error occurs) */ for (total = 0; total < buflen; ) { remaining = buflen - total; /* Have we already allocated the data block? */ if (wrfile->doffset == 0) { /* No, allocate the data block now, re-packing if necessary. */ wrfile->datlen = 0; ret = nxffs_wralloc(volume, wrfile, remaining); if (ret < 0) { fdbg("ERROR: Failed to allocate a data block: %d\n", -ret); goto errout_with_semaphore; } } /* Seek to the FLASH block containing the data block */ nxffs_ioseek(volume, wrfile->doffset); /* Verify that the FLASH data that was previously written is still intact */ ret = nxffs_reverify(volume, wrfile); if (ret < 0) { fdbg("ERROR: Failed to verify FLASH data block: %d\n", -ret); goto errout_with_semaphore; } /* Append the data to the end of the data block and write the updated * block to flash. */ nwritten = nxffs_wrappend(volume, wrfile, &buffer[total], remaining); if (nwritten < 0) { fdbg("ERROR: Failed to append to FLASH to a data block: %d\n", -ret); goto errout_with_semaphore; } /* Decrement the number of bytes remaining to be written */ total += nwritten; } /* Success.. return the number of bytes written */ ret = total; filep->f_pos = wrfile->datlen; errout_with_semaphore: sem_post(&volume->exclsem); errout: return ret; }
static inline int nxffs_wralloc(FAR struct nxffs_volume_s *volume, FAR struct nxffs_wrfile_s *wrfile, size_t size) { bool packed; int ret; /* Allocate FLASH memory for the data block. * * Loop until the data block header is configured or until a failure * occurs. Note that nothing is written to FLASH. The data block header * is not written until either (1) the file is closed, or (2) the data * region is fully populated. */ packed = false; for (;;) { size_t mindata = MIN(NXFFS_MINDATA, size); /* File a valid location to position the data block. Start with * the first byte in the free FLASH region. */ ret = nxffs_hdrpos(volume, wrfile, mindata); if (ret == OK) { /* Find a region of memory in the block that is fully erased */ ret = nxffs_hdrerased(volume, wrfile, mindata); if (ret == OK) { /* Valid memory for the data block was found. Return success. */ return OK; } } /* If no valid memory is found searching to the end of the volume, * then -ENOSPC will be returned. Other errors are not handled. */ if (ret != -ENOSPC || packed) { fdbg("ERROR: Failed to find inode header memory: %d\n", -ret); return -ENOSPC; } /* -ENOSPC is a special case.. It means that the volume is full. * Try to pack the volume in order to free up some space. */ ret = nxffs_pack(volume); if (ret < 0) { fdbg("ERROR: Failed to pack the volume: %d\n", -ret); return ret; } /* After packing the volume, froffset will be updated to point to the * new free flash region. Try again. */ nxffs_ioseek(volume, volume->froffset); packed = true; } /* Can't get here */ return OK; }
static int nxffs_rdentry(FAR struct nxffs_volume_s *volume, off_t offset, FAR struct nxffs_entry_s *entry) { struct nxffs_inode_s inode; uint32_t ecrc; uint32_t crc; uint8_t state; int namlen; int ret; DEBUGASSERT(volume && entry); memset(entry, 0, sizeof(struct nxffs_entry_s)); /* Read the header at the FLASH offset */ nxffs_ioseek(volume, offset); memcpy(&inode, &volume->cache[volume->iooffset], SIZEOF_NXFFS_INODE_HDR); /* Check if the file state is recognized. */ state = inode.state; if (state != INODE_STATE_FILE && state != INODE_STATE_DELETED) { /* This can't be a valid inode.. don't bother with the rest */ ret = -ENOENT; goto errout_no_offset; } /* Copy the packed header into the user-friendly buffer */ entry->hoffset = offset; entry->noffset = nxffs_rdle32(inode.noffs); entry->doffset = nxffs_rdle32(inode.doffs); entry->utc = nxffs_rdle32(inode.utc); entry->datlen = nxffs_rdle32(inode.datlen); /* Modify the packed header and perform the (partial) CRC calculation */ ecrc = nxffs_rdle32(inode.crc); inode.state = CONFIG_NXFFS_ERASEDSTATE; memset(inode.crc, 0, 4); crc = crc32((FAR const uint8_t *)&inode, SIZEOF_NXFFS_INODE_HDR); /* Allocate memory to hold the variable-length file name */ namlen = inode.namlen; entry->name = (FAR char *)kmalloc(namlen + 1); if (!entry->name) { fdbg("ERROR: Failed to allocate name, namlen: %d\n", namlen); ret = -ENOMEM; goto errout_no_offset; } /* Seek to the expected location of the name in FLASH */ nxffs_ioseek(volume, entry->noffset); /* Make sure that the block is in memory (the name may not be in the * same block as the inode header. */ ret = nxffs_rdcache(volume, volume->ioblock); if (ret < 0) { fdbg("ERROR: nxffsx_rdcache failed: %d\n", -ret); goto errout_with_name; } /* Read the file name from the expected offset in FLASH */ memcpy(entry->name, &volume->cache[volume->iooffset], namlen); entry->name[namlen] = '\0'; /* Finish the CRC calculation and verify the entry */ crc = crc32part((FAR const uint8_t *)entry->name, namlen, crc); if (crc != ecrc) { fdbg("ERROR: CRC entry: %08x CRC calculated: %08x\n", ecrc, crc); ret = -EIO; goto errout_with_name; } /* We have a good inode header.. but it still could a deleted file. * Check the file state. */ if (state != INODE_STATE_FILE) { /* It is a deleted file. But still, the data offset and the * start size are good so we can use this information to advance * further in FLASH memory and reduce the search time. */ offset = nxffs_inodeend(volume, entry); nxffs_freeentry(entry); ret = -ENOENT; goto errout; } /* Everything is good.. leave the offset pointing to the valid inode * header. */ return OK; /* On errors where we are suspicious of the validity of the inode header, * we need to increment the file position to just after the "good" magic * word. */ errout_with_name: nxffs_freeentry(entry); errout_no_offset: offset += NXFFS_MAGICSIZE; errout: nxffs_ioseek(volume, offset); return ret; }
int nxffs_nextentry(FAR struct nxffs_volume_s *volume, off_t offset, FAR struct nxffs_entry_s *entry) { int nmagic; int ch; int nerased; int ret; /* Seek to the first FLASH offset provided by the caller. */ nxffs_ioseek(volume, offset); /* Then begin searching */ nerased = 0; nmagic = 0; for (;;) { /* Read the next character */ ch = nxffs_getc(volume, SIZEOF_NXFFS_INODE_HDR - nmagic); if (ch < 0) { fdbg("ERROR: nxffs_getc failed: %d\n", -ch); return ch; } /* Check for another erased byte */ else if (ch == CONFIG_NXFFS_ERASEDSTATE) { /* If we have encountered NXFFS_NERASED number of consecutive * erased bytes, then presume we have reached the end of valid * data. */ if (++nerased >= NXFFS_NERASED) { fvdbg("No entry found\n"); return -ENOENT; } } else { nerased = 0; /* Check for the magic sequence indicating the start of an NXFFS * inode. There is the possibility of this magic sequnce occurring * in FLASH data. However, the header CRC should distinguish * between real NXFFS inode headers and such false alarms. */ if (ch != g_inodemagic[nmagic]) { /* Ooops... this is the not the right character for the magic * Sequence. Check if we need to restart or to cancel the sequence: */ if (ch == g_inodemagic[0]) { nmagic = 1; } else { nmagic = 0; } } else if (nmagic < NXFFS_MAGICSIZE - 1) { /* We have one more character in the magic sequence */ nmagic++; } /* We have found the magic sequence in the FLASH data that may * indicate the beginning of an NXFFS inode. */ else { /* The the FLASH offset where we found the matching magic number */ offset = nxffs_iotell(volume) - NXFFS_MAGICSIZE; /* Try to extract the inode header from that position */ ret = nxffs_rdentry(volume, offset, entry); if (ret == OK) { fvdbg("Found a valid fileheader, offset: %d\n", offset); return OK; } /* False alarm.. keep looking */ nmagic = 0; } } } /* We won't get here, but to keep some compilers happy: */ return -ENOENT; }