void strcpy16_htod(uint16_t *dst, const char16_t *src) {
    while (*src) {
        uint16_t s = htods(static_cast<uint16_t>(*src));
        *dst++ = s;
        src++;
    }
    *dst = 0;
}
void strcpy16_htod(char16_t *dst, const char16_t *src) {
    while (*src) {
        char16_t s = htods(*src);
        *dst++ = s;
        src++;
    }
    *dst = 0;
}
TEST(ResStringPool, AppendToEmptyTable) {
  const size_t header_size = sizeof(android::ResStringPool_header);
  android::ResStringPool_header header = {
      {htods(android::RES_STRING_POOL_TYPE),
       htods(header_size),
       htodl(header_size)},
      0,
      0,
      htodl(android::ResStringPool_header::UTF8_FLAG |
            android::ResStringPool_header::SORTED_FLAG),
      0,
      0};
  android::ResStringPool pool((void*)&header, header_size, false);

  pool.appendString(android::String8("Hello, world"));
  auto big_string = make_big_string(300);
  auto big_chars = big_string.c_str();
  pool.appendString(android::String8(big_chars));
  pool.appendString(android::String8("€666"));
  pool.appendString(android::String8("banana banana"));
  android::Vector<char> v;
  pool.serialize(v);

  auto data = (void*)v.array();
  android::ResStringPool after(data, v.size(), false);

  // Ensure sort bit was cleared.
  auto flags = dtohl(((android::ResStringPool_header*)data)->flags);
  ASSERT_FALSE(flags & android::ResStringPool_header::SORTED_FLAG);

  size_t out_len;
  ASSERT_STREQ(after.string8At(0, &out_len), "Hello, world");
  ASSERT_EQ(out_len, 12);
  ASSERT_STREQ(after.string8At(1, &out_len), big_chars);
  ASSERT_EQ(out_len, 300);
  ASSERT_STREQ(after.string8At(2, &out_len), "€666");
  ASSERT_STREQ(after.string8At(3, &out_len), "banana banana");
}
Example #4
0
void* ARSTREAM_Reader_RunAckThread (void *ARSTREAM_Reader_t_Param)
{
    ARSTREAM_NetworkHeaders_AckPacket_t sendPacket = {0};
    ARSTREAM_Reader_t *reader = (ARSTREAM_Reader_t *)ARSTREAM_Reader_t_Param;
    memset(&sendPacket, 0, sizeof(sendPacket));

    ARSAL_PRINT (ARSAL_PRINT_DEBUG, ARSTREAM_READER_TAG, "Ack sender thread running");
    reader->ackThreadStarted = 1;

    while (reader->threadsShouldStop == 0)
    {
        int isPeriodicAck = 0;
        ARSAL_Mutex_Lock (&(reader->ackSendMutex));
        if (reader->maxAckInterval <= 0)
        {
            ARSAL_Cond_Wait (&(reader->ackSendCond), &(reader->ackSendMutex));
        }
        else
        {
            int retval = ARSAL_Cond_Timedwait (&(reader->ackSendCond), &(reader->ackSendMutex), reader->maxAckInterval);
            if (retval == -1 && errno == ETIMEDOUT)
            {
                isPeriodicAck = 1;
            }
        }
        ARSAL_Mutex_Unlock (&(reader->ackSendMutex));

        /* Only send an ACK if the maxAckInterval value allows it. */
        if ((reader->maxAckInterval > 0) ||
            ((reader->maxAckInterval == 0) && (isPeriodicAck == 0)))
        {
            ARSAL_Mutex_Lock (&(reader->ackPacketMutex));
            sendPacket.frameNumber = htods  (reader->ackPacket.frameNumber);
            sendPacket.highPacketsAck = htodll (reader->ackPacket.highPacketsAck);
            sendPacket.lowPacketsAck  = htodll (reader->ackPacket.lowPacketsAck);
            ARSAL_Mutex_Unlock (&(reader->ackPacketMutex));
            ARNETWORK_Manager_SendData (reader->manager, reader->ackBufferID, (uint8_t *)&sendPacket, sizeof (sendPacket), NULL, ARSTREAM_Reader_NetworkCallback, 1);
        }
    }

    ARSAL_PRINT (ARSAL_PRINT_DEBUG, ARSTREAM_READER_TAG, "Ack sender thread ended");
    reader->ackThreadStarted = 0;
    return (void *)0;
}
Example #5
0
status_t StringPool::writeStringBlock(const sp<AaptFile>& pool)
{
    // Allow appending.  Sorry this is a little wacky.
    if (pool->getSize() > 0) {
        sp<AaptFile> block = createStringBlock();
        if (block == NULL) {
            return UNKNOWN_ERROR;
        }
        ssize_t res = pool->writeData(block->getData(), block->getSize());
        return (res >= 0) ? (status_t)NO_ERROR : res;
    }

    // First we need to add all style span names to the string pool.
    // We do this now (instead of when the span is added) so that these
    // will appear at the end of the pool, not disrupting the order
    // our client placed their own strings in it.
    
    const size_t STYLES = mEntryStyleArray.size();
    size_t i;

    for (i=0; i<STYLES; i++) {
        entry_style& style = mEntryStyleArray.editItemAt(i);
        const size_t N = style.spans.size();
        for (size_t i=0; i<N; i++) {
            entry_style_span& span = style.spans.editItemAt(i);
            ssize_t idx = add(span.name, true);
            if (idx < 0) {
                fprintf(stderr, "Error adding span for style tag '%s'\n",
                        String8(span.name).string());
                return idx;
            }
            span.span.name.index = (uint32_t)idx;
        }
    }

    const size_t ENTRIES = mEntryArray.size();

    // Now build the pool of unique strings.

    const size_t STRINGS = mEntries.size();
    const size_t preSize = sizeof(ResStringPool_header)
                         + (sizeof(uint32_t)*ENTRIES)
                         + (sizeof(uint32_t)*STYLES);
    if (pool->editData(preSize) == NULL) {
        fprintf(stderr, "ERROR: Out of memory for string pool\n");
        return NO_MEMORY;
    }

    const size_t charSize = mUTF8 ? sizeof(uint8_t) : sizeof(uint16_t);

    size_t strPos = 0;
    for (i=0; i<STRINGS; i++) {
        entry& ent = mEntries.editItemAt(i);
        const size_t strSize = (ent.value.size());
        const size_t lenSize = strSize > (size_t)(1<<((charSize*8)-1))-1 ?
            charSize*2 : charSize;

        String8 encStr;
        if (mUTF8) {
            encStr = String8(ent.value);
        }

        const size_t encSize = mUTF8 ? encStr.size() : 0;
        const size_t encLenSize = mUTF8 ?
            (encSize > (size_t)(1<<((charSize*8)-1))-1 ?
                charSize*2 : charSize) : 0;

        ent.offset = strPos;

        const size_t totalSize = lenSize + encLenSize +
            ((mUTF8 ? encSize : strSize)+1)*charSize;

        void* dat = (void*)pool->editData(preSize + strPos + totalSize);
        if (dat == NULL) {
            fprintf(stderr, "ERROR: Out of memory for string pool\n");
            return NO_MEMORY;
        }
        dat = (uint8_t*)dat + preSize + strPos;
        if (mUTF8) {
            uint8_t* strings = (uint8_t*)dat;

            ENCODE_LENGTH(strings, sizeof(uint8_t), strSize)

            ENCODE_LENGTH(strings, sizeof(uint8_t), encSize)

            strncpy((char*)strings, encStr, encSize+1);
        } else {
            uint16_t* strings = (uint16_t*)dat;

            ENCODE_LENGTH(strings, sizeof(uint16_t), strSize)

            strcpy16_htod(strings, ent.value);
        }

        strPos += totalSize;
    }

    // Pad ending string position up to a uint32_t boundary.

    if (strPos&0x3) {
        size_t padPos = ((strPos+3)&~0x3);
        uint8_t* dat = (uint8_t*)pool->editData(preSize + padPos);
        if (dat == NULL) {
            fprintf(stderr, "ERROR: Out of memory padding string pool\n");
            return NO_MEMORY;
        }
        memset(dat+preSize+strPos, 0, padPos-strPos);
        strPos = padPos;
    }

    // Build the pool of style spans.

    size_t styPos = strPos;
    for (i=0; i<STYLES; i++) {
        entry_style& ent = mEntryStyleArray.editItemAt(i);
        const size_t N = ent.spans.size();
        const size_t totalSize = (N*sizeof(ResStringPool_span))
                               + sizeof(ResStringPool_ref);

        ent.offset = styPos-strPos;
        uint8_t* dat = (uint8_t*)pool->editData(preSize + styPos + totalSize);
        if (dat == NULL) {
            fprintf(stderr, "ERROR: Out of memory for string styles\n");
            return NO_MEMORY;
        }
        ResStringPool_span* span = (ResStringPool_span*)(dat+preSize+styPos);
        for (size_t i=0; i<N; i++) {
            span->name.index = htodl(ent.spans[i].span.name.index);
            span->firstChar = htodl(ent.spans[i].span.firstChar);
            span->lastChar = htodl(ent.spans[i].span.lastChar);
            span++;
        }
        span->name.index = htodl(ResStringPool_span::END);

        styPos += totalSize;
    }

    if (STYLES > 0) {
        // Add full terminator at the end (when reading we validate that
        // the end of the pool is fully terminated to simplify error
        // checking).
        size_t extra = sizeof(ResStringPool_span)-sizeof(ResStringPool_ref);
        uint8_t* dat = (uint8_t*)pool->editData(preSize + styPos + extra);
        if (dat == NULL) {
            fprintf(stderr, "ERROR: Out of memory for string styles\n");
            return NO_MEMORY;
        }
        uint32_t* p = (uint32_t*)(dat+preSize+styPos);
        while (extra > 0) {
            *p++ = htodl(ResStringPool_span::END);
            extra -= sizeof(uint32_t);
        }
        styPos += extra;
    }

    // Write header.

    ResStringPool_header* header =
        (ResStringPool_header*)pool->padData(sizeof(uint32_t));
    if (header == NULL) {
        fprintf(stderr, "ERROR: Out of memory for string pool\n");
        return NO_MEMORY;
    }
    memset(header, 0, sizeof(*header));
    header->header.type = htods(RES_STRING_POOL_TYPE);
    header->header.headerSize = htods(sizeof(*header));
    header->header.size = htodl(pool->getSize());
    header->stringCount = htodl(ENTRIES);
    header->styleCount = htodl(STYLES);
    if (mUTF8) {
        header->flags |= htodl(ResStringPool_header::UTF8_FLAG);
    }
    header->stringsStart = htodl(preSize);
    header->stylesStart = htodl(STYLES > 0 ? (preSize+strPos) : 0);

    // Write string index array.

    uint32_t* index = (uint32_t*)(header+1);
    for (i=0; i<ENTRIES; i++) {
        entry& ent = mEntries.editItemAt(mEntryArray[i]);
        *index++ = htodl(ent.offset);
        NOISY(printf("Writing entry #%d: \"%s\" ent=%d off=%d\n", i,
                String8(ent.value).string(),
                mEntryArray[i], ent.offset));
    }

    // Write style index array.

    for (i=0; i<STYLES; i++) {
        *index++ = htodl(mEntryStyleArray[i].offset);
    }

    return NO_ERROR;
}
Example #6
0
#if 0  // No longer compiles
    NOISY(printf("Found strings:\n");
        const size_t N = strings.size();
        for (size_t i=0; i<N; i++) {
            printf("%s\n", String8(strings.entryAt(i).string).string());
        }
    );
#endif    

    sp<AaptFile> stringPool = strings.createStringBlock();
    NOISY(aout << "String pool:"
          << HexDump(stringPool->getData(), stringPool->getSize()) << endl);

    ResXMLTree_header header;
    memset(&header, 0, sizeof(header));
    header.header.type = htods(RES_XML_TYPE);
    header.header.headerSize = htods(sizeof(header));

    const size_t basePos = dest->getSize();
    dest->writeData(&header, sizeof(header));
    dest->writeData(stringPool->getData(), stringPool->getSize());

    // If we have resource IDs, write them.
    if (resids.size() > 0) {
        const size_t resIdsPos = dest->getSize();
        const size_t resIdsSize =
            sizeof(ResChunk_header)+(sizeof(uint32_t)*resids.size());
        ResChunk_header* idsHeader = (ResChunk_header*)
            (((const uint8_t*)dest->editData(resIdsPos+resIdsSize))+resIdsPos);
        idsHeader->type = htods(RES_XML_RESOURCE_MAP_TYPE);
        idsHeader->headerSize = htods(sizeof(*idsHeader));
status_t StringPool::writeStringBlock(const sp<AaptFile>& pool)
{
    // Allow appending.  Sorry this is a little wacky.
    if (pool->getSize() > 0) {
        sp<AaptFile> block = createStringBlock();
        if (block == NULL) {
            return UNKNOWN_ERROR;
        }
        ssize_t res = pool->writeData(block->getData(), block->getSize());
        return (res >= 0) ? (status_t)NO_ERROR : res;
    }

    // First we need to add all style span names to the string pool.
    // We do this now (instead of when the span is added) so that these
    // will appear at the end of the pool, not disrupting the order
    // our client placed their own strings in it.
    
    const size_t STYLES = mEntryStyleArray.size();
    size_t i;

    for (i=0; i<STYLES; i++) {
        entry_style& style = mEntryStyleArray.editItemAt(i);
        const size_t N = style.spans.size();
        for (size_t i=0; i<N; i++) {
            entry_style_span& span = style.spans.editItemAt(i);
            ssize_t idx = add(span.name, true);
            if (idx < 0) {
                fprintf(stderr, "Error adding span for style tag '%s'\n",
                        String8(span.name).string());
                return idx;
            }
            span.span.name.index = (uint32_t)idx;
        }
    }

    const size_t ENTRIES = size();

    // Now build the pool of unique strings.

    const size_t STRINGS = mEntries.size();
    const size_t preSize = sizeof(ResStringPool_header)
                         + (sizeof(uint32_t)*ENTRIES)
                         + (sizeof(uint32_t)*STYLES);
    if (pool->editData(preSize) == NULL) {
        fprintf(stderr, "ERROR: Out of memory for string pool\n");
        return NO_MEMORY;
    }

    size_t strPos = 0;
    for (i=0; i<STRINGS; i++) {
        entry& ent = mEntries.editItemAt(i);
        const size_t strSize = (ent.value.size());
        const size_t lenSize = strSize > 0x7fff ? sizeof(uint32_t) : sizeof(uint16_t);
        const size_t totalSize = lenSize + ((strSize+1)*sizeof(uint16_t));

        ent.offset = strPos;
        uint16_t* dat = (uint16_t*)pool->editData(preSize + strPos + totalSize);
        if (dat == NULL) {
            fprintf(stderr, "ERROR: Out of memory for string pool\n");
            return NO_MEMORY;
        }
        dat += (preSize+strPos)/sizeof(uint16_t);
        if (lenSize > sizeof(uint16_t)) {
            *dat = htods(0x8000 | ((strSize>>16)&0x7fff));
            dat++;
        }
        *dat++ = htods(strSize);
        strcpy16_htod(dat, ent.value);

        strPos += lenSize + (strSize+1)*sizeof(uint16_t);
    }
Example #8
0
MFS_DIR_ENTRY_PTR MFS_Create_directory_entry
    (
    MFS_DRIVE_STRUCT_PTR    drive_ptr,  /*[IN] the drive on which to operate */
    char_ptr        filename_ptr,       /*[IN] pointer to the file's name */
    char            attribute,          /*[IN] attribute to be assigned to the file */
    uint_32_ptr     dir_cluster_ptr,    /*[IN/OUT] indicates cluster in which the entry was put.*/
    uint_32_ptr     dir_index_ptr,      /*[IN/OUT] index of the location of new file within the directory */
    uint_32_ptr     error_ptr           /*[IN/OUT] error_return_address */
    )
{
    MFS_DIR_ENTRY_PTR     dir_entry_ptr, dir_entry_ptr1;
    MFS_DIR_ENTRY_PTR     temp_entry_ptr;
    TIME_STRUCT           time;
    DATE_STRUCT           clk_time;
    uint_32               dir_index, saved_index, temp_index;
    uint_32               entry_cluster, saved_cluster, temp_cluster;
    uint_32               needed_entries, i;
    int_32                length;
    boolean               found_all = FALSE;
    char                  temp_name[SFILENAME_SIZE+1], short_name[SFILENAME_SIZE+1];  
    uchar                 checksum = 0;
    uint_32               prev_cluster= CLUSTER_INVALID, saved_prev_cluster, temp_prev_cluster; 

    dir_entry_ptr = NULL;
    entry_cluster = *dir_cluster_ptr;
    dir_index = 0;

    /*
    ** Check for duplicate file
    */
    if ( filename_ptr == NULL )
    {
        *error_ptr = MFS_INVALID_PARAMETER;
    }
    else if ( *filename_ptr == '\0' )
    {
        *error_ptr = MFS_FILE_NOT_FOUND;
    }
#if MFSCFG_READ_ONLY_CHECK
    else if (MFS_is_read_only (NULL, drive_ptr))
    {
        *error_ptr = MFS_DISK_IS_WRITE_PROTECTED;
    }
#endif
    else if ( (dir_entry_ptr = MFS_Find_directory_entry(drive_ptr, filename_ptr,
        &entry_cluster, &dir_index, &prev_cluster, MFS_ATTR_ANY, error_ptr)) != NULL )
    {
        *error_ptr = MFS_FILE_EXISTS;
    }
    else
    {
        /*
        ** Search for an empty slot
        */

        dir_index = 0;
        dir_entry_ptr = MFS_Find_directory_entry(drive_ptr, "", &entry_cluster, &dir_index, &prev_cluster, attribute, error_ptr); 


        if ( MFS_Dirname_valid(filename_ptr) )
        {
            found_all = TRUE;
        }

        if ( dir_entry_ptr ==  NULL && !*error_ptr )
        {
            found_all = TRUE;
        }

        /* 
        ** Save it now because we might not go into the while loop
        ** If we don't go into while, we lose original values when these 
        ** variables are restored after the while loop
        */
        saved_cluster = entry_cluster;
        saved_index = dir_index;

        while ( !found_all )
        {
            /* Calculate the amount of extra entries needed for LFN's */
            saved_cluster = entry_cluster;
            saved_index = dir_index;

            for ( needed_entries = (strlen(filename_ptr) + 12) / 13;  needed_entries >= 1 && !found_all; needed_entries-- )
            {
                *error_ptr = MFS_Increment_dir_index(drive_ptr, &entry_cluster, &dir_index, NULL);  
                if ( entry_cluster == CLUSTER_EOF && !*error_ptr )
                {
                    /* This means the LFN will span a cluster. */
                    found_all = TRUE;
                    break;
                }
                temp_index = dir_index;
                temp_cluster = entry_cluster;
                temp_entry_ptr = MFS_Find_directory_entry(drive_ptr, "", &entry_cluster, &dir_index, &prev_cluster, MFS_ATTR_ANY,error_ptr); 
                if ( *error_ptr )
                {
                    return NULL;
                }
                if ( !temp_entry_ptr )
                {
                    found_all = TRUE;
                    dir_entry_ptr = NULL;
                    break;
                }
                else if ( dir_index == temp_index && temp_cluster == entry_cluster )
                {
                    continue;
                }
                else
                {
                    break;
                }        
            }  

            if ( needed_entries == 0 )
            {
                found_all = TRUE;
            }

            if ( found_all )
            {
                dir_entry_ptr = MFS_Find_directory_entry(drive_ptr, "", &saved_cluster, &saved_index, &saved_prev_cluster, MFS_ATTR_ANY,error_ptr); 
            }
        }  

        entry_cluster = saved_cluster;
        dir_index = saved_index;


        if ( dir_entry_ptr ==  NULL && !*error_ptr )
        {
            /*
            ** Empty spot not found... get a new cluster and use the first entry.
            */
            dir_index = 0;
            if ( entry_cluster == 0 )
            {
                *error_ptr = MFS_ROOT_DIR_FULL;
                return(NULL);
            }
            else
            {
                *error_ptr = MFS_Add_cluster_to_chain(drive_ptr, entry_cluster, &entry_cluster);
                if ( *error_ptr == MFS_NO_ERROR )
                {
                    *error_ptr = MFS_Clear_cluster(drive_ptr, entry_cluster);
                    if ( *error_ptr )
                    {
                        return(NULL);
                    }
                    dir_entry_ptr = (pointer) drive_ptr->DIR_SECTOR_PTR;
                    *error_ptr = MFS_Write_back_fat(drive_ptr);
                    if ( *error_ptr )
                    {
                        return(NULL);
                    }
                }
            }  
        }
    }  

    if ( *error_ptr == MFS_NO_ERROR )
    {
        /* At this point we have one of the following cases 
        **
        ** 1. We have a normal 8.3 filename and an empty slot for it
        ** 2. We have a LFN, and a slot which has enough consecutive free slots
        **    to store the LFN, followed by the actual entry
        */

        /* If the file is a normal 8.3 file */
        if ( MFS_Dirname_valid(filename_ptr) )
        {
            _mem_zero(dir_entry_ptr, sizeof (MFS_DIR_ENTRY));            
            htodc(dir_entry_ptr->ATTRIBUTE, attribute);
            MFS_Expand_dotfile(filename_ptr, dir_entry_ptr->NAME);

            _time_get(&time);
            _time_to_date(&time, &clk_time);
            NORMALIZE_DATE(&clk_time);
            htods(dir_entry_ptr->TIME, PACK_TIME(clk_time));
            htods(dir_entry_ptr->DATE, PACK_DATE(clk_time));   
            drive_ptr->DIR_SECTOR_DIRTY = TRUE;
        }
        else
        {
            /* Case where the file is a LFN */   
            length = strlen(filename_ptr);

            /* Get the 8.3 name and calculate the checksum */
            temp_index = 0;
            temp_cluster = *dir_cluster_ptr;
            *error_ptr = MFS_lfn_to_sfn(filename_ptr, temp_name);

            if ( !*error_ptr )
            {
                do
                {
                    dir_entry_ptr1 = MFS_Find_directory_entry(drive_ptr, temp_name, &temp_cluster, &temp_index, &temp_prev_cluster, MFS_ATTR_ANY, error_ptr); 
                    if ( !*error_ptr && dir_entry_ptr1 != NULL )
                    {
                        MFS_increment_lfn(temp_name);
                        temp_index = 0;       
                    }
                } while ( !*error_ptr && dir_entry_ptr1 != NULL );
                dir_entry_ptr = MFS_Find_directory_entry(drive_ptr, "", &entry_cluster, &dir_index, &prev_cluster, attribute, error_ptr); 

                /*
                ** The memory should be cleared after finding the directory entry.
                */
                _mem_zero(dir_entry_ptr, sizeof (MFS_DIR_ENTRY));

                /* Setup the final slot */
                ((MFS_LNAME_ENTRY_PTR) dir_entry_ptr)->ID |= MFS_LFN_END;
                drive_ptr->DIR_SECTOR_DIRTY = TRUE;

                MFS_Expand_dotfile(temp_name,short_name);
                checksum = MFS_lfn_checksum(short_name);
            }

            while ( length && !*error_ptr )
            {
                i = length - ((length -1) % 13 + 1);
                *error_ptr = MFS_lfn_name_to_entry(filename_ptr + i, (MFS_LNAME_ENTRY_PTR) dir_entry_ptr);

                /* Set the entry number, the checksum value and the attribute */
                ((MFS_LNAME_ENTRY_PTR) dir_entry_ptr)->ID |= (length + 12) / 13;
                ((MFS_LNAME_ENTRY_PTR) dir_entry_ptr)->ALIAS_CHECKSUM= checksum;
                ((MFS_LNAME_ENTRY_PTR) dir_entry_ptr)->ATTR = MFS_ATTR_LFN;
                drive_ptr->DIR_SECTOR_DIRTY = TRUE;

                length -= (length - i);
                if ( length < 0 )
                {
                    length = 0;
                }

                if ( length && !*error_ptr )
                {

                    dir_entry_ptr = MFS_Find_directory_entry(drive_ptr, "", &entry_cluster, &dir_index, &prev_cluster, attribute, error_ptr); 
                    if ( dir_entry_ptr ==  NULL && !*error_ptr )
                    {
                        /* No empty spots... We need to get a new cluster */
                        dir_index = 0; 
                        if ( entry_cluster == 0 )
                        {
                            *error_ptr = MFS_ROOT_DIR_FULL; 
                            return(NULL); 
                        }
                        else
                        {
                            *error_ptr = MFS_Add_cluster_to_chain( drive_ptr, entry_cluster, &entry_cluster); 
                            if ( *error_ptr == MFS_NO_ERROR )
                            {
                                *error_ptr = MFS_Clear_cluster(drive_ptr, entry_cluster); 
                                if ( *error_ptr )
                                {
                                    return(NULL); 
                                }
                                dir_entry_ptr = (pointer) drive_ptr->DIR_SECTOR_PTR; 
                                *error_ptr = MFS_Write_back_fat(drive_ptr); 
                                if ( *error_ptr )
                                {
                                    return(NULL); 
                                }
                            }
                        }   
                    }
                    _mem_zero(dir_entry_ptr, sizeof (MFS_DIR_ENTRY));
                    drive_ptr->DIR_SECTOR_DIRTY = TRUE;
                }
            }  

            /* LFN is written, so write the actual entry */
            if ( !*error_ptr )
            {
                dir_entry_ptr = MFS_Find_directory_entry(drive_ptr, "", &entry_cluster, &dir_index, &prev_cluster, attribute, error_ptr); 
                if ( dir_entry_ptr == NULL && !*error_ptr )
                {
                    dir_index = 0; 
                    if ( entry_cluster == 0 )
                    {
                        *error_ptr = MFS_ROOT_DIR_FULL; 
                        return(NULL); 
                    }
                    else
                    {
                        *error_ptr = MFS_Add_cluster_to_chain( drive_ptr, entry_cluster, &entry_cluster); 
                        if ( *error_ptr == MFS_NO_ERROR )
                        {
                            *error_ptr =  MFS_Clear_cluster(drive_ptr, entry_cluster); 
                            if ( *error_ptr )
                            {
                                return(NULL); 
                            }
                            dir_entry_ptr = (pointer) drive_ptr->DIR_SECTOR_PTR; 
                            *error_ptr = MFS_Write_back_fat(drive_ptr); 
                            if ( *error_ptr )
                            {
                                return(NULL); 
                            }
                        }
                    }   
                }

                _mem_zero(dir_entry_ptr, sizeof (MFS_DIR_ENTRY));
                htodc(dir_entry_ptr->ATTRIBUTE, attribute);
                MFS_Expand_dotfile(temp_name, dir_entry_ptr->NAME);

                _time_get(&time);
                _time_to_date(&time, &clk_time);
                NORMALIZE_DATE(&clk_time);
                htods(dir_entry_ptr->TIME, PACK_TIME(clk_time));
                htods(dir_entry_ptr->DATE, PACK_DATE(clk_time));   
                drive_ptr->DIR_SECTOR_DIRTY = TRUE;
            }
        }  

        if ( *error_ptr )
        {
            return(NULL);
        }
    }

    *dir_cluster_ptr = entry_cluster;
    *dir_index_ptr = dir_index;
    return(dir_entry_ptr);
}