BlobStorage::Blob BlobStorage::add(const String& path, const Data& data)
{
    ASSERT(!RunLoop::isMain());

    auto hash = computeSHA1(data);
    if (data.isEmpty())
        return { data, hash };

    auto blobPath = WebCore::fileSystemRepresentation(blobPathForHash(hash));
    auto linkPath = WebCore::fileSystemRepresentation(path);
    unlink(linkPath.data());

    bool blobExists = access(blobPath.data(), F_OK) != -1;
    if (blobExists) {
        auto existingData = mapFile(blobPath.data());
        if (bytesEqual(existingData, data)) {
            link(blobPath.data(), linkPath.data());
            return { existingData, hash };
        }
        unlink(blobPath.data());
    }

    auto mappedData = data.mapToFile(blobPath.data());
    if (mappedData.isNull())
        return { };

    link(blobPath.data(), linkPath.data());

    m_approximateSize += mappedData.size();

    return { mappedData, hash };
}
BlobStorage::Blob BlobStorage::add(const String& path, const Data& data)
{
    ASSERT(!RunLoop::isMain());

    auto hash = computeSHA1(data);
    if (data.isEmpty())
        return { data, hash };

    auto blobPath = WebCore::fileSystemRepresentation(blobPathForHash(hash));
    auto linkPath = WebCore::fileSystemRepresentation(path);
    unlink(linkPath.data());

    bool blobExists = access(blobPath.data(), F_OK) != -1;
    if (blobExists) {
        auto existingData = mapFile(blobPath.data());
        if (bytesEqual(existingData, data)) {
            link(blobPath.data(), linkPath.data());
            return { existingData, hash };
        }
        unlink(blobPath.data());
    }

    int fd = open(blobPath.data(), O_CREAT | O_EXCL | O_RDWR , S_IRUSR | S_IWUSR);
    if (fd < 0)
        return { };

    size_t size = data.size();
    if (ftruncate(fd, size) < 0) {
        close(fd);
        return { };
    }

    void* map = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    close(fd);

    if (map == MAP_FAILED)
        return { };

    uint8_t* mapData = static_cast<uint8_t*>(map);
    data.apply([&mapData](const uint8_t* bytes, size_t bytesSize) {
        memcpy(mapData, bytes, bytesSize);
        mapData += bytesSize;
        return true;
    });

    // Drop the write permission.
    mprotect(map, size, PROT_READ);

    auto mappedData = Data::adoptMap(map, size);

    link(blobPath.data(), linkPath.data());

    m_approximateSize += size;

    return { mappedData, hash };
}