From patchwork Tue Sep 4 07:49:36 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: AKASHI Takahiro X-Patchwork-Id: 145859 Delivered-To: patch@linaro.org Received: by 2002:a2e:1648:0:0:0:0:0 with SMTP id 8-v6csp3367295ljw; Tue, 4 Sep 2018 00:56:43 -0700 (PDT) X-Google-Smtp-Source: ANB0VdZ9HOIPwoRoCB9xwuOdWOYIhxoEu7QN2os3+E4SYdC74CLm21xE/lBqZVKqLUqzPlHA0/yB X-Received: by 2002:a50:a7e2:: with SMTP id i89-v6mr34907514edc.176.1536047803295; Tue, 04 Sep 2018 00:56:43 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1536047803; cv=none; d=google.com; s=arc-20160816; b=UMmjDLljLb/LEFjk+eZXVKI/14tL6D6ql/q1sQz7mjmhfXkPYeighGCwwQcS+WNyFs FkQA8Upqc0S0VVNbwxxQ2SDjCuZdOyy7JnCn9D3q+c5TNj9Z44UV+I+hBhqxntoN6jRx w30fgbWxdiIfEWJ2TXCy/JYA1czzHDlDVG7f7coANiqW7fSjDLK3cyf5YcEPHAzD/ACy BLCL24NUqfYAlgPRSOC/EST7hAJGp2XfwkKuXDxYwE7wc6GQk2gFUu2Lgt4U9GK+nRYV qJhq3XK/Wc9sEQXt74O9cnjL46cH/hrTlan2VehCE1xVwOrb++yvyuz2Y8H/gC2t12VK KpJg== 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=EiSNEH/irg+h5lXKlq2ZXgIGWlGdICNichzaJGlv2Ic=; b=Qp86w+oTooHDGK8VOgSdgO3l9Z8Jp4Qii3otjLdY+u0a6Fw/5EFIKWQ08ZFQjqRKvM sl9g44BLPfXwzMmwiICU+5jwAm9757ZHylvmPg22/3Nrg7WutVmG4riHR7TBVxkQsvLV nMF9/+nj8XMwOmXe72XYiNSDc9pce/+upwsDIHl9+m+Qr6ZsngbhdTsMFBVrJaMwvoHk 07FdIpcHvJGOaiLinndNf49Y/hwnkQEJvu2eNcnDXWMY4V+3FNeU9XgFkXh/9ZnMHOem bBzY+TX7j449gtTeayb9lfMPY2l/Ogd9GsAivDuVu9w5XssoQZMjsOXmjNxTg2DztyU9 ETsQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@linaro.org header.s=google header.b=Zd7832KA; 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 c8si4530807edw.301.2018.09.04.00.56.43; Tue, 04 Sep 2018 00:56:43 -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=Zd7832KA; 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 D6AABC21F32; Tue, 4 Sep 2018 07:54:35 +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 D91D3C21E4E; Tue, 4 Sep 2018 07:53:33 +0000 (UTC) Received: by lists.denx.de (Postfix, from userid 105) id 9EA03C21F32; Tue, 4 Sep 2018 07:51:47 +0000 (UTC) Received: from mail-pg1-f193.google.com (mail-pg1-f193.google.com [209.85.215.193]) by lists.denx.de (Postfix) with ESMTPS id 1C858C21F2A for ; Tue, 4 Sep 2018 07:51:47 +0000 (UTC) Received: by mail-pg1-f193.google.com with SMTP id x26-v6so1236997pge.12 for ; Tue, 04 Sep 2018 00:51:47 -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=RjGLMDA1zC9xnkIUTqYOyNjFaMCbA7j71lV4+hyJf2s=; b=Zd7832KADnH8BdLtQB3Y/hEpWXheugOmKYxI/Db0j0gaJiFrQNv1DnRAotm9G8WzdO f9/AenQx0Jz24HHjFyh95o9CDwyLmf23aRjxAJxKRQid0yD1BoLJ4mcHCxIkxbkJtWLC An+UwZzLtRkUUbUvF1WucBxGTIP4y7QwBVrrk= 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=RjGLMDA1zC9xnkIUTqYOyNjFaMCbA7j71lV4+hyJf2s=; b=QC7ywH3uL+rfZpl/+WbiydK20Vs+VK3zdUeSGKjx/mmeWoWhfXgVueaWM6yRqFkdHC 4FX/8agGdCZLDKuNDdyswDVQLh6K2oi+cLPOZzLZYwQRLdbhNVYfJ0HuxwBN5qo3JOLh 6c97rRCbfSmLKpt0h4inqhtMz4Sldet1IeiQ4G1EUOdn84WizlHnSr5WBQaE5WvT8sct qNsfzqxPq8gFdLG/K/EhiIslrsKEql8T0LBlovYRjaAAEghrQd8iB7Xxlss9B55Fzqnp qmPJRKCYVCUMwEeS2lK/xWe677HyJM2FO8XVPBvHGaC38hGj0ZSdgQT/u/uRPZxN/2av SPdQ== X-Gm-Message-State: APzg51CrWRKvkPfO8k2y/FVPJHH43E6sWos9GbdQldq+igovcLwz2Ce9 3bShuXdfJcMM/NSAyzXi7zAP6A== X-Received: by 2002:a63:fc07:: with SMTP id j7-v6mr27763184pgi.1.1536047505708; Tue, 04 Sep 2018 00:51:45 -0700 (PDT) Received: from linaro.org ([121.95.100.191]) by smtp.googlemail.com with ESMTPSA id b21-v6sm53522428pfm.97.2018.09.04.00.51.44 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Tue, 04 Sep 2018 00:51:45 -0700 (PDT) From: AKASHI Takahiro To: trini@konsulko.com Date: Tue, 4 Sep 2018 16:49:36 +0900 Message-Id: <20180904074948.18146-12-takahiro.akashi@linaro.org> X-Mailer: git-send-email 2.18.0 In-Reply-To: <20180904074948.18146-1-takahiro.akashi@linaro.org> References: <20180904074948.18146-1-takahiro.akashi@linaro.org> Cc: xypron.glpk@gmx.de, agraf@suse.de, u-boot@lists.denx.de Subject: [U-Boot] [PATCH v2 11/23] 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 maintain compatibility with the current interface. Signed-off-by: AKASHI Takahiro --- fs/fat/fat_write.c | 288 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 273 insertions(+), 15 deletions(-) diff --git a/fs/fat/fat_write.c b/fs/fat/fat_write.c index 5db7830ad43c..523abd62c9e9 100644 --- a/fs/fat/fat_write.c +++ b/fs/fat/fat_write.c @@ -450,6 +450,121 @@ 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 */ @@ -578,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; @@ -849,6 +1096,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 { @@ -869,6 +1126,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 */ @@ -918,10 +1181,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); }