From patchwork Fri Jul 20 02:57:15 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: AKASHI Takahiro X-Patchwork-Id: 142418 Delivered-To: patch@linaro.org Received: by 2002:a2e:9754:0:0:0:0:0 with SMTP id f20-v6csp2440675ljj; Thu, 19 Jul 2018 20:04:13 -0700 (PDT) X-Google-Smtp-Source: AAOMgpcnf/6fYYN17VvPUTsSSk1/q9hp7ymgVIEu4BSzS5mim27L4xexQ9eJy2zKVmwixd+DGEKx X-Received: by 2002:a50:a584:: with SMTP id a4-v6mr553086edc.289.1532055853334; Thu, 19 Jul 2018 20:04:13 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1532055853; cv=none; d=google.com; s=arc-20160816; b=QZ5/aFasBHZTDnsagf3STtyHAmeq033+b97cYm3WJq8HztLkJj5renGPqwdH0H9u5m vyVB7+L5Bt8eWLAHrLq2hbiIvDq+bh8+seOdBac7Gsb/QZmcstsWku9uOV8AQgyjV0MF 9Hz2yiG2S2m4g9U3WQg/w26VFYastA83NZ1oOmglxTl0TPYTPQfa34DhfT5r9TE1YipF G8nEqLZinclozf2Ps7xWXhQerszy1JDASBKhpzfkV67xYL+u7ScGHq+te46pjX4LxPNi 4isEug2Ek2x9z9neCYD5KJuqr+8XjsnVVs93xj3KZ/0kVQlyQsxlh8BgalHwQnYBquJR mQHg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:mime-version :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:cc:references:in-reply-to:message-id :date:to:from:dkim-signature:arc-authentication-results; bh=2EY2NFdgtBgUhkWqkDMGw0GymsLVUaQxXeN8PRAESN4=; b=ic4pnMTuW9bffLC+OiT5NryBcAfQqh9G7oNig2bWihBZKoJrJ6P2I59LWnVado6wiH ALvpQD3Ie2fS5NIghzVwqyx8YW8TMtaH/LTfS9bxV6HwhLOCl8hVNm7BL/U4cU/Wg3MU lEBd80pFvgYVCtpPHTsJY9a9vRD2QqqJMIYg46S03ZwlnVE8YmFC6nHrukK4xSr7XCeM HZq0LpZLqzdcs9bYLq/L/A9vzDrcJK9k3jIyf6iH36hRZev9G8S4KzUpXIk+U7G5i5zX 8amScMWb0/WXZPaxfdgSgliZktWcBkLuxXsHwUWgluDScgv2Vo548KMS2UOF9/sc6+4q 7uog== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@linaro.org header.s=google header.b=ZWtCBWZz; spf=pass (google.com: best guess record for domain of u-boot-bounces@lists.denx.de designates 81.169.180.215 as permitted sender) smtp.mailfrom=u-boot-bounces@lists.denx.de; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=linaro.org Return-Path: Received: from lists.denx.de (dione.denx.de. [81.169.180.215]) by mx.google.com with ESMTP id y67-v6si205195ede.433.2018.07.19.20.04.13; Thu, 19 Jul 2018 20:04:13 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of u-boot-bounces@lists.denx.de designates 81.169.180.215 as permitted sender) client-ip=81.169.180.215; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@linaro.org header.s=google header.b=ZWtCBWZz; spf=pass (google.com: best guess record for domain of u-boot-bounces@lists.denx.de designates 81.169.180.215 as permitted sender) smtp.mailfrom=u-boot-bounces@lists.denx.de; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: by lists.denx.de (Postfix, from userid 105) id BDF65C21DA2; Fri, 20 Jul 2018 03:01:03 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on lists.denx.de X-Spam-Level: X-Spam-Status: No, score=-0.0 required=5.0 tests=RCVD_IN_MSPIKE_H3, RCVD_IN_MSPIKE_WL, T_DKIM_INVALID autolearn=unavailable autolearn_force=no version=3.4.0 Received: from lists.denx.de (localhost [IPv6:::1]) by lists.denx.de (Postfix) with ESMTP id 76A33C21E36; Fri, 20 Jul 2018 03:00:46 +0000 (UTC) Received: by lists.denx.de (Postfix, from userid 105) id B2962C21E36; Fri, 20 Jul 2018 02:57:57 +0000 (UTC) Received: from mail-pl0-f68.google.com (mail-pl0-f68.google.com [209.85.160.68]) by lists.denx.de (Postfix) with ESMTPS id 9BBEFC21DFB for ; Fri, 20 Jul 2018 02:57:53 +0000 (UTC) Received: by mail-pl0-f68.google.com with SMTP id z7-v6so2735465plo.9 for ; Thu, 19 Jul 2018 19:57:53 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=uTLOBoWrhW6WAFoX6WJPZT9BhXSiyH8UiiiGDYgVgkQ=; b=ZWtCBWZzor4Yr2VDvL72p9iU9KgN8Qd0KxJphX+mFUIdSHcwLlyQ4UJogZS7cjLoIu OxFAxhnMZp8BchhXu6r1w0qvi+2koWyo+/ayExyxBzU+aiShqcZCQ+/lRWef8PWT37TT GbjKOjBNATbnWr1lbq5P8hgOyEJtYQxFeH2y8= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=uTLOBoWrhW6WAFoX6WJPZT9BhXSiyH8UiiiGDYgVgkQ=; b=p8MFwPl2wzTwI6qpD/FEZNZSgSCMNlgALCkyleVTbumrkxAQLIos+D9htyvZIahML7 cpv5p9Fh+Vr++wV6mDrSZIX/nQ8LuY1U+gqhf7nAPQH7YxygRgxHnDXZnjKu9kkl169s WKCEAv8p2msNGzYbfxtcZeD4oBz3em0D41ZNOUWCu//utnNzlZ3dGo3OV7cMFxi7fbRh E+YpotC9FoUMOpKsUfqUl99Zbd++aa3OEOo4YY0EHHTE977Aclq3t7oyNuHzveXfcxXO apJuv8jNZoyjDKpATkli1W4Eo2E5AphIfdHLa/mpb+7Nu1XYw7PNZOrkXgpXAQp8oCzZ 0JwA== X-Gm-Message-State: AOUpUlHbACV56T4KU33NqggUBz5G00ohgH+PzHP2LtRjEBu1ji7E40DW U2h4OAslMDfkVmK3HElzKar1SQ== X-Received: by 2002:a17:902:d70d:: with SMTP id w13-v6mr325252ply.40.1532055472238; Thu, 19 Jul 2018 19:57:52 -0700 (PDT) Received: from linaro.org ([121.95.100.191]) by smtp.googlemail.com with ESMTPSA id l3-v6sm638786pff.8.2018.07.19.19.57.50 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Thu, 19 Jul 2018 19:57:51 -0700 (PDT) From: AKASHI Takahiro To: trini@konsulko.com Date: Fri, 20 Jul 2018 11:57:15 +0900 Message-Id: <20180720025723.6736-10-takahiro.akashi@linaro.org> X-Mailer: git-send-email 2.17.0 In-Reply-To: <20180720025723.6736-1-takahiro.akashi@linaro.org> References: <20180720025723.6736-1-takahiro.akashi@linaro.org> Cc: u-boot@lists.denx.de, xypron.glpk@gmx.de, agraf@suse.de Subject: [U-Boot] [PATCH 09/17] fs: fat: support write with non-zero offset X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.18 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" In this patch, all the necessary code for allowing for a file offset at write is implemented. What plays a major roll here is get_set_cluster(), which, in contrast to its counterpart, set_cluster(), only operates on already-allocated clusters, overwriting with data. So, with a file offset specified, set_contents() seeks and writes data with set_get_cluster() until the end of a file, and, once it reaches there, continues writing with set_cluster() for the rest. Please note that a file will be trimmed as a result of write operation if write ends before reaching file's end. This is an intended behavior in order to maitain compatibility with the current interface. Signed-off-by: AKASHI Takahiro --- fs/fat/fat_write.c | 287 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 272 insertions(+), 15 deletions(-) diff --git a/fs/fat/fat_write.c b/fs/fat/fat_write.c index 3a9c53e253..cc45a33876 100644 --- a/fs/fat/fat_write.c +++ b/fs/fat/fat_write.c @@ -450,6 +450,120 @@ set_cluster(fsdata *mydata, __u32 clustnum, __u8 *buffer, return 0; } +static __u8 tmpbuf_cluster[MAX_CLUSTSIZE] __aligned(ARCH_DMA_MINALIGN); + +/* + * Read and modify data on existing and consecutive cluster blocks + */ +static int +get_set_cluster(fsdata *mydata, __u32 clustnum, loff_t pos, __u8 *buffer, + loff_t size, loff_t *gotsize) +{ + unsigned int bytesperclust = mydata->clust_size * mydata->sect_size; + __u32 startsect; + loff_t wsize; + int clustcount, i, ret; + + *gotsize = 0; + if (!size) + return 0; + + assert(pos < bytesperclust); + startsect = clust_to_sect(mydata, clustnum); + + debug("clustnum: %d, startsect: %d, pos: %lld\n", clustnum, startsect, + pos); + + /* partial write at beginning */ + if (pos) { + wsize = min(bytesperclust - pos, size); + ret = disk_read(startsect, mydata->clust_size, tmpbuf_cluster); + if (ret != mydata->clust_size) { + debug("Error reading data (got %d)\n", ret); + return -1; + } + + memcpy(tmpbuf_cluster + pos, buffer, wsize); + ret = disk_write(startsect, mydata->clust_size, tmpbuf_cluster); + if (ret != mydata->clust_size) { + debug("Error writing data (got %d)\n", ret); + return -1; + } + + size -= wsize; + buffer += wsize; + *gotsize += wsize; + + startsect += mydata->clust_size; + + if (!size) + return 0; + } + + /* full-cluster write */ + if (size >= bytesperclust) { + clustcount = lldiv(size, bytesperclust); + + if (!((unsigned long)buffer & (ARCH_DMA_MINALIGN - 1))) { + wsize = clustcount * bytesperclust; + ret = disk_write(startsect, + clustcount * mydata->clust_size, + buffer); + if (ret != clustcount * mydata->clust_size) { + debug("Error writing data (got %d)\n", ret); + return -1; + } + + size -= wsize; + buffer += wsize; + *gotsize += wsize; + + startsect += clustcount * mydata->clust_size; + } else { + for (i = 0; i < clustcount; i++) { + memcpy(tmpbuf_cluster, buffer, bytesperclust); + ret = disk_write(startsect, mydata->clust_size, + tmpbuf_cluster); + if (ret != mydata->clust_size) { + debug("Error writing data (got %d)\n", + ret); + return -1; + } + + size -= bytesperclust; + buffer += bytesperclust; + *gotsize += bytesperclust; + + startsect += mydata->clust_size; + } + } + } + + /* partial write at end */ + if (size) { + wsize = size; + ret = disk_read(startsect, mydata->clust_size, tmpbuf_cluster); + if (ret != mydata->clust_size) { + debug("Error reading data (got %d)\n", ret); + return -1; + } + memcpy(tmpbuf_cluster, buffer, wsize); + ret = disk_write(startsect, mydata->clust_size, tmpbuf_cluster); + if (ret != mydata->clust_size) { + debug("Error writing data (got %d)\n", ret); + return -1; + } + + size -= wsize; + buffer += wsize; + *gotsize += wsize; + } + + assert(!size); + + return 0; +} + /* * Find the first empty cluster */ @@ -579,26 +693,158 @@ set_contents(fsdata *mydata, dir_entry *dentptr, loff_t pos, __u8 *buffer, unsigned int bytesperclust = mydata->clust_size * mydata->sect_size; __u32 curclust = START(dentptr); __u32 endclust = 0, newclust = 0; - loff_t actsize; + loff_t cur_pos, offset, actsize, wsize; *gotsize = 0; - filesize = maxsize; + filesize = pos + maxsize; debug("%llu bytes\n", filesize); - if (curclust) { - /* - * release already-allocated clusters anyway - */ - if (clear_fatent(mydata, curclust)) { - printf("Error: clearing FAT entries\n"); + if (!filesize) { + if (!curclust) + return 0; + if (!CHECK_CLUST(curclust, mydata->fatsize) || + IS_LAST_CLUST(curclust, mydata->fatsize)) { + clear_fatent(mydata, curclust); + set_start_cluster(mydata, dentptr, 0); + return 0; + } + debug("curclust: 0x%x\n", curclust); + debug("Invalid FAT entry\n"); + return -1; + } + + if (!curclust) { + assert(pos == 0); + goto set_clusters; + } + + /* go to cluster at pos */ + cur_pos = bytesperclust; + while (1) { + if (pos <= cur_pos) + break; + if (IS_LAST_CLUST(curclust, mydata->fatsize)) + break; + + newclust = get_fatent(mydata, curclust); + if (!IS_LAST_CLUST(newclust, mydata->fatsize) && + CHECK_CLUST(newclust, mydata->fatsize)) { + debug("curclust: 0x%x\n", curclust); + debug("Invalid FAT entry\n"); return -1; } + + cur_pos += bytesperclust; + curclust = newclust; + } + if (IS_LAST_CLUST(curclust, mydata->fatsize)) { + assert(pos == cur_pos); + goto set_clusters; } - curclust = find_empty_cluster(mydata); - set_start_cluster(mydata, dentptr, curclust); + assert(pos < cur_pos); + cur_pos -= bytesperclust; + /* overwrite */ + assert(IS_LAST_CLUST(curclust, mydata->fatsize) || + !CHECK_CLUST(curclust, mydata->fatsize)); + + while (1) { + /* search for allocated consecutive clusters */ + actsize = bytesperclust; + endclust = curclust; + while (1) { + if (filesize <= (cur_pos + actsize)) + break; + + newclust = get_fatent(mydata, endclust); + + if (IS_LAST_CLUST(newclust, mydata->fatsize)) + break; + if (CHECK_CLUST(newclust, mydata->fatsize)) { + debug("curclust: 0x%x\n", curclust); + debug("Invalid FAT entry\n"); + return -1; + } + + actsize += bytesperclust; + endclust = newclust; + } + + /* overwrite to */ + if (pos < cur_pos) + offset = 0; + else + offset = pos - cur_pos; + wsize = min(cur_pos + actsize, filesize) - pos; + if (get_set_cluster(mydata, curclust, offset, buffer, wsize, + &actsize)) { + printf("Error get-and-setting cluster\n"); + return -1; + } + buffer += wsize; + *gotsize += wsize; + cur_pos += offset + wsize; + + if (filesize <= cur_pos) + break; + + /* CHECK: newclust = get_fatent(mydata, endclust); */ + + if (IS_LAST_CLUST(newclust, mydata->fatsize)) + /* no more clusters */ + break; + + curclust = newclust; + } + + if (filesize <= cur_pos) { + /* no more write */ + newclust = get_fatent(mydata, endclust); + if (!IS_LAST_CLUST(newclust, mydata->fatsize)) { + /* truncate the rest */ + clear_fatent(mydata, newclust); + + /* Mark end of file in FAT */ + if (mydata->fatsize == 12) + newclust = 0xfff; + else if (mydata->fatsize == 16) + newclust = 0xffff; + else if (mydata->fatsize == 32) + newclust = 0xfffffff; + set_fatent_value(mydata, endclust, newclust); + } + + return 0; + } + + curclust = endclust; + filesize -= cur_pos; + assert(!(cur_pos % bytesperclust)); + +set_clusters: + /* allocate and write */ + assert(!pos); + + /* Assure that curclust is valid */ + if (!curclust) { + curclust = find_empty_cluster(mydata); + set_start_cluster(mydata, dentptr, curclust); + } else { + newclust = get_fatent(mydata, curclust); + + if (IS_LAST_CLUST(newclust, mydata->fatsize)) { + newclust = determine_fatent(mydata, curclust); + set_fatent_value(mydata, curclust, newclust); + curclust = newclust; + } else { + debug("error: something wrong\n"); + return -1; + } + } + + /* TODO: already partially written */ if (check_overflow(mydata, curclust, filesize)) { printf("Error: no space left: %llu\n", filesize); return -1; @@ -852,6 +1098,16 @@ int file_fat_write_at(const char *filename, loff_t pos, void *buffer, goto exit; } + /* A file exists */ + if (pos == -1) + /* Append to the end */ + pos = FAT2CPU32(retdent->size); + if (pos > retdent->size) { + /* No hole allowed */ + ret = -EINVAL; + goto exit; + } + /* Update file size in a directory entry */ retdent->size = cpu_to_le32(pos + size); } else { @@ -872,6 +1128,12 @@ int file_fat_write_at(const char *filename, loff_t pos, void *buffer, goto exit; } + if (pos) { + /* No hole allowed */ + ret = -EINVAL; + goto exit; + } + memset(itr->dent, 0, sizeof(*itr->dent)); /* Set short name to set alias checksum field in dir_slot */ @@ -921,10 +1183,5 @@ exit: int file_fat_write(const char *filename, void *buffer, loff_t offset, loff_t maxsize, loff_t *actwrite) { - if (offset != 0) { - printf("Error: non zero offset is currently not supported.\n"); - return -EINVAL; - } - return file_fat_write_at(filename, offset, buffer, maxsize, actwrite); }