int _PutFile()

in lsvmutils/vfat.c [1324:1521]


int _PutFile(
    VFAT* vfat,
    BOOLEAN isDir,
    const char* path,
    void* data,
    UINTN size)
{
    int rc = -1;
    VFATDirectoryEntry ent;
    char dirname[VFAT_PATH_SIZE];
    char basename[VFAT_PATH_SIZE];
    void* dirData = NULL;
    UINTN dirSize;
    UINT32 dirClustno;
    UINT32 clustno = 0;
    const VFATDirectoryEntry dot =
    {
        { '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ' }, /* name */
        ATTR_ARCHIVE | ATTR_DIRECTORY, /* attr */
        0, /* res */
        0, /* crtTimeTenth */
        1709, /* crtTime */
        18706, /* crtDate */
        18706, /* lstAccDate */
        0, /* fstClusHI */
        22131, /* wrtTime */
        18706, /* wrtDate */
        0, /* fstClusLO */
        0, /* fileSize */
    };
    const VFATDirectoryEntry dotdot =
    {
        { '.', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ' }, /* name */
        ATTR_ARCHIVE | ATTR_DIRECTORY, /* attr */
        0, /* res */
        0, /* crtTimeTenth */
        1709, /* crtTime */
        18706, /* crtDate */
        18706, /* lstAccDate */
        0, /* fstClusHI */
        22131, /* wrtTime */
        18706, /* wrtDate */
        0, /* fstClusLO */
        0, /* fileSize */
    };

    /* Check parameters */
    if (!vfat || !path || !data)
        GOTO(done);

    /* If a directory, then copy "." and ".." entries */
    if (isDir)
    {
        Memset(data, 0, size);
        Memcpy(data, &dot, sizeof(dot));
        Memcpy((UINT8*)data + sizeof(dot), &dotdot, sizeof(dotdot));
    }

    /* Fail if file already exists */
    if (VFATStatFile(vfat, path, &ent) == 0)
        GOTO(done);

    /* Reject non-absolute paths */
    if (path[0] != '/')
        GOTO(done);

    /* Get the base name from the path (final component) */
    if (_GetBaseName(basename, path) == NULL)
        GOTO(done);

    /* Get the directory name from the path (excludes final component) */
    if (_GetDirName(dirname, path) == NULL)
        GOTO(done);

    /* Load directory into memory */
    if (_LoadDir(vfat, dirname, &dirData, &dirSize, &dirClustno) != 0)
        GOTO(done);

    /* If creating directory file, update the parent directory cluster */
    if (isDir)
    {
        VFATDirectoryEntry* p = (VFATDirectoryEntry*)data;
        p[1].fstClusHI = (dirClustno & 0xFFFF0000) << 16;
        p[1].fstClusLO = (dirClustno & 0x0000FFFF);
    }

    /* Inject directory entry */
    {
        /* Used hardcoded dates and times */
        VFATDirectoryEntry entry =
        {
            { '\0' }, /* name */
            0, /* attr */
            0, /* res */
            100, /* crtTimeTenth */
            1884, /* crtTime */
            18706, /* crtDate */
            18706, /* lstAccDate */
            0, /* fstClusHI */
            1884, /* wrtTime */
            18706, /* wrtDate */
            0, /* fstClusLO */
            0, /* fileSize */
        };

        /* Set VFATDirectoryEntry.attr */
        {
            entry.attr = ATTR_ARCHIVE;

            if (isDir)
                entry.attr |= ATTR_DIRECTORY;
        }

        /* Set VFATDirectoryEntry.name */
        {
            char shortname[12];
            InternShortname(shortname, basename);
            Memcpy(entry.name, shortname, sizeof(entry.name));
        }

        /* Set VFATDirectoryEntry.fileSize */
        if (!isDir)
            entry.fileSize = size;

        /* Allocate FAT chain for this new file (update in-memory FAT copy) */
        {
            UINTN r = (size + vfat->ClusterSize - 1) / vfat->ClusterSize;

            if (_AllocateFATChain(vfat, r, &clustno) != 0)
                GOTO(done);
        }

        /* If creating directory file, update the directory cluster */
        if (isDir)
        {
            VFATDirectoryEntry* p = (VFATDirectoryEntry*)data;
            p[0].fstClusHI = (clustno & 0xFFFF0000) << 16;
            p[0].fstClusLO = (clustno & 0x0000FFFF);
        }

        /* Update directory entry cluster number */
        entry.fstClusHI = (clustno & 0xFFFF0000) << 16;
        entry.fstClusLO = (clustno & 0x0000FFFF);

        /* Inject entry into directory file memory */
        {
            VFATDirectoryEntry* p = (VFATDirectoryEntry*)dirData;

            for (; p->name[0]; p++)
            {
                /* Use this entry if deleted */
                if (p->name[0] == 0xE5)
                    break;

                /* Skip long-name entries */
                if (p->attr == ATTR_LONG_NAME)
                    continue;
            }

            /* If no more room for a directory entry */
            if ((void*)(p + 1) >= (dirData + dirSize))
                GOTO(done);

            /* Update the entry */
            Memcpy(p, &entry, sizeof(*p));
        }
    }

    /* Flush file to disk */
    if (_FlushFile(vfat, data, size, clustno) != 0)
        GOTO(done);

    /* If this is the root directory, then refresh the cache */
    if (dirClustno == vfat->rootdirClustno)
    {
        if (vfat->rootdir.size != dirSize)
            goto done;

        Memcpy(vfat->rootdir.data, dirData, dirSize);
    }

    /* Flush directory to disk */
    if (_FlushFile(vfat, dirData, dirSize, dirClustno) != 0)
        GOTO(done);

    /* Flush FAT to disk */
    if (_FlushFAT(vfat))
        GOTO(done);

    rc = 0;

done:

    if (dirData)
        Free(dirData);

    return rc;
}