From patchwork Tue Sep 11 06:59:06 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: AKASHI Takahiro X-Patchwork-Id: 146409 Delivered-To: patch@linaro.org Received: by 2002:a2e:1648:0:0:0:0:0 with SMTP id 8-v6csp3316435ljw; Tue, 11 Sep 2018 00:06:32 -0700 (PDT) X-Google-Smtp-Source: ANB0Vdb/Uuw0LNdjExO94qMeyWb0L9luuZANNunnSJ5ys1gbZOvGpm8SmY3cC8i9091ODnz8IxLq X-Received: by 2002:aa7:c441:: with SMTP id n1-v6mr26005828edr.55.1536649591951; Tue, 11 Sep 2018 00:06:31 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1536649591; cv=none; d=google.com; s=arc-20160816; b=rVmop6aEzbhmNQCF26i0RjM84400K/cFJmFTGsvSttgOicsMS+eiCZZoQ95SB1Ad/+ SbKva5LrgMY9Us2O+koju8DnxCNoPmdOTuMwS133g3pLuHC6WD5t+4Q87BoIePa57p4K tUya9bdAC46g3pXCXmCb2Zwv1XBjXBi6sE1dCDHPhNdsRQ2o3gPnzmcoj3vLsAs+pd3f BjiQEsvYuzFgtGv2aukC36oFsPFHg4AAn1eJV8f2G0CSSGsFmofdC/W05X3VePNcuMHG /Xm3KuWL+7d+9qRhLzjiax2wlyjWgB+QEHvCHnrIjVEiX3ExOvEc7kv82aLcbSIIcWkm NZSw== 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; bh=Ct99BLiCJieLrybxk3KjGFheYtGbcOoDzGlGG+dfDb8=; b=B0Q1in53GOWLHBxwRdF4TG6U4KXSzvXmMmH7F2UEChe8V1W+rVGulMyuEnSy0hD9LO mTlgZnioM7iqGVe/PW/2Hfg32eKzwh+rSxiiIUQC8QSXELXPlB2ltOh/Hc3DmdhchxR4 hUFed+lnpwGo2e8agif4w7GklinSGqyillaC/eD8BZAKbWOpH5vYr76HRGqem0RJ8TJu PwYbBOJ0UF3iSvNV0zyQ0E5OUdOZalM9pwRJTFdAGLqTdZ5eEUTKz6BubI8A47TuQA/n 80WFeZFgBmDRpsjuVulY7j+1Bp/Z15IHqFCGjb4dZuNeAbuqyWWzXIwkJ/1ez/GnbQcQ t5ug== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@linaro.org header.s=google header.b=Xdt2mCKS; 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 k1-v6si7223669edf.127.2018.09.11.00.06.31; Tue, 11 Sep 2018 00:06:31 -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=Xdt2mCKS; 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 5A4C4C21E76; Tue, 11 Sep 2018 07:04:40 +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 670E5C21ECF; Tue, 11 Sep 2018 07:00:15 +0000 (UTC) Received: by lists.denx.de (Postfix, from userid 105) id 86A2BC21F13; Tue, 11 Sep 2018 06:59:48 +0000 (UTC) Received: from mail-pg1-f194.google.com (mail-pg1-f194.google.com [209.85.215.194]) by lists.denx.de (Postfix) with ESMTPS id 45249C21E77 for ; Tue, 11 Sep 2018 06:59:41 +0000 (UTC) Received: by mail-pg1-f194.google.com with SMTP id r1-v6so11735574pgp.11 for ; Mon, 10 Sep 2018 23:59:41 -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=zNRV+sAGYlpd4MCnwOpkz+vhTS1NAfDl0YaTIOD1cug=; b=Xdt2mCKS367V4uALrFoFpgPmAZpwS/LIKZn2qd5eQDlZ1nEHAEaStx9WxS9+23g/dq H8VR8EZ7+2qS5rsbRT6GeT8XjNtgu86qHJKqr7gUHc89+MZRC3g0qzM3wKpo2DUgKB3x KL3XUF+8DhPqeQuNyuggcYgra20PTN/NBOvAQ= 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=zNRV+sAGYlpd4MCnwOpkz+vhTS1NAfDl0YaTIOD1cug=; b=VxdbMqPM3yS20U7SnmjFPs+zd4jE2LpY+lY6HKpBZDCkZUvwF3FigZ4oHpBYEqzhmx r5LCRFmC8i7GP39r00kRPOyc5NHjZ4ZwTvdFBq0QJ+nILlhjiRIUATtpYSSfnqv7FSee YUbzELckXHfYfnEQwT1lXVcNuIP+CU6CP2ioDXMUB5ZWV8d4wjd+MKPIRm9NOxFmFF0y lXqg40DdhNGt7hCsKSgOcvpg42Me04gDT8LXa7BN03vpVwqHnPEfK70KeQ99MvnPsWtV S+L3CBgsafJboDmndInPSGAkQG+/c0cAchRu3S1lUBqK5r3Rafrv7AAU0A5qr67G3EuV 5yNQ== X-Gm-Message-State: APzg51BYlbRqpb1yMbuFtZt9uD4C5mSQuNkVVPPzbeuZbZL7+aCSd6yO ZzBeF3NQOpbYW/UVtCMACmsD/w== X-Received: by 2002:a62:6b49:: with SMTP id g70-v6mr27763488pfc.91.1536649179813; Mon, 10 Sep 2018 23:59:39 -0700 (PDT) Received: from linaro.org ([121.95.100.191]) by smtp.googlemail.com with ESMTPSA id f186-v6sm18793426pgc.4.2018.09.10.23.59.38 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Mon, 10 Sep 2018 23:59:39 -0700 (PDT) From: "Akashi, Takahiro" To: trini@konsulko.com Date: Tue, 11 Sep 2018 15:59:06 +0900 Message-Id: <20180911065922.19141-11-takahiro.akashi@linaro.org> X-Mailer: git-send-email 2.18.0 In-Reply-To: <20180911065922.19141-1-takahiro.akashi@linaro.org> References: <20180911065922.19141-1-takahiro.akashi@linaro.org> Cc: u-boot@lists.denx.de, xypron.glpk@gmx.de, agraf@suse.de Subject: [U-Boot] [PATCH v3 10/26] 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" From: AKASHI Takahiro 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 c22d8c7a46a1..651c7866debc 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); }