From nobody Tue Apr 7 06:02:27 2026 Received: from mail-yw1-f173.google.com (mail-yw1-f173.google.com [209.85.128.173]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 15CA233066D for ; Mon, 16 Mar 2026 02:20:04 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.173 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773627610; cv=none; b=PuUXnKRfQ6g2LWgNd/rIyeh5Jc27kCtblfUTv+gpdDm7MPAsKO/yrvDDq1r56SZj+nP+XLwUYqV8pSUQqDz04gGGEvU//n6jzGwmzc2ddqpOyJG9bMBrnbi62dWLVAzBAktybhSozHSN1l7Xk4B7f3MfYlTNDzjJGrSYk/LGHBo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773627610; c=relaxed/simple; bh=yfOWOLSksPqGoJAJfci2KCmYxJ24EATyvZByhUurvvc=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=WJzBU0jEv+fV/UnyPGYhrvZDl4vNaVYbAcfwlV+t/1wGYu9cI3CMf0vFL3S0YINNp2lV8JjabpX+mucRfL1ke8j1LNP0bHTfGMmyMFojcGOZ9kUGGFJLEGLcIpZ05hKvtdyyclGNXtZTS3YO19lyMR5WOGFM8x8yrDYUfkNdhrc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=dubeyko.com; spf=pass smtp.mailfrom=dubeyko.com; dkim=pass (2048-bit key) header.d=dubeyko-com.20230601.gappssmtp.com header.i=@dubeyko-com.20230601.gappssmtp.com header.b=Lu+eflu3; arc=none smtp.client-ip=209.85.128.173 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=dubeyko.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=dubeyko.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=dubeyko-com.20230601.gappssmtp.com header.i=@dubeyko-com.20230601.gappssmtp.com header.b="Lu+eflu3" Received: by mail-yw1-f173.google.com with SMTP id 00721157ae682-79860421382so35549787b3.0 for ; Sun, 15 Mar 2026 19:20:04 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=dubeyko-com.20230601.gappssmtp.com; s=20230601; t=1773627604; x=1774232404; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=QJBvqc9WEOvthtT77vz6VJiRddJGTHusrraoDZ4T9uQ=; b=Lu+eflu37PpdXbWOutsLb0bcKiPBFCC1xvOKyJ9/Wsl9K5zZ3KBLIBQKUpgtakJhqf tR4c3dMk9fZuc3HJaAkcm5QlV/OZKXvPOhtKiI9bzWvJifrR7k6sNE1RgIteXyvysgas +r/eE9XjAJFzJQFWgWlhRhkMf4rQ3VjGUtje7O1ULa6sWIi0H7tKKQUGz4vd1lxIQcj6 VxvtZvezoEnlL3cXtmLmO+h0+YP5KeYkWhrIO7GINawbR/kdwFrIFYYwcOhRn0hc7gcn uMHn/AD8qJSe2CH97aE5yPf22RifAnSGMa3oh0yaOIxMC1215cz8hsi6DnVckNiB/98r lcCw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1773627604; x=1774232404; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=QJBvqc9WEOvthtT77vz6VJiRddJGTHusrraoDZ4T9uQ=; b=pNnXYvn3sPuytqGK/qqD5jZsCH0nssfQgk+lp2DanICQNVq2uaPa7HGlEmjGj5WlGb HimWzNRcJ/dPE4oElW57OTwoeX6+nOKVaXbvkr9BnqBPG5IxcgR3IUsemq17HtABcFzX tY1fNqpo8PlKJRWsJOYW/rmc48gRwwqZNT60JEOvhjQuuycDlLNY8rjDIMA/hO6pKljC k7+w49B4puCFw+KO9piSYe0oVVf8GK1Btb/lHEtKsgxDlLygfKqp3mR51EP2kIM4Zyy9 hjmq2rPrA35TGwchYbfqagVs3WGMLyDr+9G34lyTdO/f3OjBGODPT0uoYaju+EDAbprX f5Wg== X-Gm-Message-State: AOJu0Yxo11UB6R19vuY0+PMDBxEQp15jMkeK/uv7rLGQDQ7x5WZnY37F BfRTCTVOl7BjMMV569ZAGLbIhap4TDBjj3PatJYJ4+8Zz7ibGUD0jXObieYczSXKi1A= X-Gm-Gg: ATEYQzyFjPpD6b3ES/AUx9occ7Avrq3j6c3vtOATeGQg+ziFuP5FHS5OctdIdIpG4sx JD3JZPw2XlzVwevlPO5QH5WSydvxNJvfm/A1FWFcx3qDB3+XDaz+ZUBxnGllfO4dPC1VvnE58lW kFcE04l0lTnTrVqpxLkQFVRVlpwUiwrwGk8dh5vocrBV2JSsIphXUhWHtliIQTcmJwEonjtITw+ qAu6C2QGYkrQBYx5Oxi/1ezQGE1bv84tAxCLLLZEojgt8rN69j97sCRbFc0ZCcB8yu1rSW0WG/6 DwXFBkXlFOvELOMOWIzSFJ1PcCH/R3prS/Op/RIQx+dSBJ2GluYCz+i7ZAHIpM/VFCS7ocjRfYf vM/QdOVTjFqhECV4t5tV44IU4oDcSdWcbbZq0JA6px0DmbmNQpP/E41rxjpSiRvQLJau5WO6TyI B1aPzLRw+50fnXgrBWNABcZ/kuNBndQNNpsO/MWMLKVbFZrRphMv304idlPy/cVWGs5hr1NHKp9 CCSpJJ8Y8SCoQKj1mg9oyyw X-Received: by 2002:a05:690c:a751:b0:799:198d:8c65 with SMTP id 00721157ae682-79a1c0e251cmr84083617b3.18.1773627603895; Sun, 15 Mar 2026 19:20:03 -0700 (PDT) Received: from pop-os.attlocal.net ([2600:1700:6476:1430:6939:3e01:5e8f:6093]) by smtp.gmail.com with ESMTPSA id 00721157ae682-79917deb69asm79721617b3.10.2026.03.15.19.20.02 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 15 Mar 2026 19:20:03 -0700 (PDT) From: Viacheslav Dubeyko To: linux-fsdevel@vger.kernel.org Cc: linux-kernel@vger.kernel.org, Viacheslav Dubeyko Subject: [PATCH v2 75/79] ssdfs: implement inode operations support Date: Sun, 15 Mar 2026 19:17:58 -0700 Message-ID: <20260316021800.1694650-32-slava@dubeyko.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260316021800.1694650-1-slava@dubeyko.com> References: <20260316021800.1694650-1-slava@dubeyko.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Complete patchset is available here: https://github.com/dubeyko/ssdfs-driver/tree/master/patchset/linux-kernel-6= .18.0 Patch implements operations: (1) ssdfs_read_inode - read raw inode (2) ssdfs_iget - get existing inode (3) ssdfs_new_inode - create new inode (4) ssdfs_getattr - get attributes (5) ssdfs_setattr - set attributes (6) ssdfs_truncate - truncate file (7) ssdfs_setsize - set file size (8) ssdfs_evict_inode - evict inode (9) ssdfs_write_inode - store dirty inode Signed-off-by: Viacheslav Dubeyko --- fs/ssdfs/inode.c | 1262 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1262 insertions(+) create mode 100644 fs/ssdfs/inode.c diff --git a/fs/ssdfs/inode.c b/fs/ssdfs/inode.c new file mode 100644 index 000000000000..959e733b6e18 --- /dev/null +++ b/fs/ssdfs/inode.c @@ -0,0 +1,1262 @@ +/* + * SPDX-License-Identifier: BSD-3-Clause-Clear + * + * SSDFS -- SSD-oriented File System. + * + * fs/ssdfs/inode.c - inode handling routines. + * + * Copyright (c) 2014-2019 HGST, a Western Digital Company. + * http://www.hgst.com/ + * Copyright (c) 2014-2026 Viacheslav Dubeyko + * http://www.ssdfs.org/ + * + * (C) Copyright 2014-2019, HGST, Inc., All rights reserved. + * + * Created by HGST, San Jose Research Center, Storage Architecture Group + * + * Authors: Viacheslav Dubeyko + * + * Acknowledgement: Cyril Guyot + * Zvonimir Bandic + */ + +#include +#include +#include +#include +#include +#include + +#include "peb_mapping_queue.h" +#include "peb_mapping_table_cache.h" +#include "folio_vector.h" +#include "ssdfs.h" +#include "request_queue.h" +#include "btree_search.h" +#include "btree_node.h" +#include "btree.h" +#include "extents_tree.h" +#include "inodes_tree.h" +#include "dentries_tree.h" +#include "xattr_tree.h" +#include "acl.h" +#include "xattr.h" + +#include + +#ifdef CONFIG_SSDFS_MEMORY_LEAKS_ACCOUNTING +atomic64_t ssdfs_inode_folio_leaks; +atomic64_t ssdfs_inode_memory_leaks; +atomic64_t ssdfs_inode_cache_leaks; +#endif /* CONFIG_SSDFS_MEMORY_LEAKS_ACCOUNTING */ + +/* + * void ssdfs_inode_cache_leaks_increment(void *kaddr) + * void ssdfs_inode_cache_leaks_decrement(void *kaddr) + * void *ssdfs_inode_kmalloc(size_t size, gfp_t flags) + * void *ssdfs_inode_kzalloc(size_t size, gfp_t flags) + * void *ssdfs_inode_kcalloc(size_t n, size_t size, gfp_t flags) + * void ssdfs_inode_kfree(void *kaddr) + * struct folio *ssdfs_inode_alloc_folio(gfp_t gfp_mask, + * unsigned int order) + * struct folio *ssdfs_inode_add_batch_folio(struct folio_batch *batch, + * unsigned int order) + * void ssdfs_inode_free_folio(struct folio *folio) + * void ssdfs_inode_folio_batch_release(struct folio_batch *batch) + */ +#ifdef CONFIG_SSDFS_MEMORY_LEAKS_ACCOUNTING + SSDFS_MEMORY_LEAKS_CHECKER_FNS(inode) +#else + SSDFS_MEMORY_ALLOCATOR_FNS(inode) +#endif /* CONFIG_SSDFS_MEMORY_LEAKS_ACCOUNTING */ + +void ssdfs_inode_memory_leaks_init(void) +{ +#ifdef CONFIG_SSDFS_MEMORY_LEAKS_ACCOUNTING + atomic64_set(&ssdfs_inode_folio_leaks, 0); + atomic64_set(&ssdfs_inode_memory_leaks, 0); + atomic64_set(&ssdfs_inode_cache_leaks, 0); +#endif /* CONFIG_SSDFS_MEMORY_LEAKS_ACCOUNTING */ +} + +void ssdfs_inode_check_memory_leaks(void) +{ +#ifdef CONFIG_SSDFS_MEMORY_LEAKS_ACCOUNTING + if (atomic64_read(&ssdfs_inode_folio_leaks) !=3D 0) { + SSDFS_ERR("INODE: " + "memory leaks include %lld folios\n", + atomic64_read(&ssdfs_inode_folio_leaks)); + } + + if (atomic64_read(&ssdfs_inode_memory_leaks) !=3D 0) { + SSDFS_ERR("INODE: " + "memory allocator suffers from %lld leaks\n", + atomic64_read(&ssdfs_inode_memory_leaks)); + } + + if (atomic64_read(&ssdfs_inode_cache_leaks) !=3D 0) { + SSDFS_ERR("INODE: " + "caches suffers from %lld leaks\n", + atomic64_read(&ssdfs_inode_cache_leaks)); + } +#endif /* CONFIG_SSDFS_MEMORY_LEAKS_ACCOUNTING */ +} + +bool is_raw_inode_checksum_correct(struct ssdfs_fs_info *fsi, + void *buf, size_t size) +{ + struct ssdfs_inode *raw_inode; + size_t raw_inode_size; + __le32 old_checksum; + bool is_valid =3D false; + + spin_lock(&fsi->inodes_tree->lock); + raw_inode_size =3D fsi->inodes_tree->raw_inode_size; + spin_unlock(&fsi->inodes_tree->lock); + + if (raw_inode_size !=3D size) { + SSDFS_WARN("raw_inode_size %zu !=3D size %zu\n", + raw_inode_size, size); + return false; + } + +#ifdef CONFIG_SSDFS_DEBUG + SSDFS_DBG("RAW INODE DUMP:\n"); + print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, + buf, size); + SSDFS_DBG("\n"); +#endif /* CONFIG_SSDFS_DEBUG */ + + raw_inode =3D (struct ssdfs_inode *)buf; + + old_checksum =3D raw_inode->checksum; + raw_inode->checksum =3D 0; + raw_inode->checksum =3D ssdfs_crc32_le(buf, size); + + is_valid =3D old_checksum =3D=3D raw_inode->checksum; + + if (!is_valid) { + SSDFS_WARN("invalid inode's checksum: " + "stored %#x !=3D calculated %#x\n", + le32_to_cpu(old_checksum), + le32_to_cpu(raw_inode->checksum)); + raw_inode->checksum =3D old_checksum; + } + + return is_valid; +} + +void ssdfs_set_inode_flags(struct inode *inode) +{ + unsigned int flags =3D SSDFS_I(inode)->flags; + unsigned int new_fl =3D 0; + + if (flags & FS_SYNC_FL) + new_fl |=3D S_SYNC; + if (flags & FS_APPEND_FL) + new_fl |=3D S_APPEND; + if (flags & FS_IMMUTABLE_FL) + new_fl |=3D S_IMMUTABLE; + if (flags & FS_NOATIME_FL) + new_fl |=3D S_NOATIME; + if (flags & FS_DIRSYNC_FL) + new_fl |=3D S_DIRSYNC; + inode_set_flags(inode, new_fl, S_SYNC | S_APPEND | S_IMMUTABLE | + S_NOATIME | S_DIRSYNC); +} + +static int ssdfs_inode_setops(struct inode *inode) +{ + if (S_ISREG(inode->i_mode)) { + inode->i_op =3D &ssdfs_file_inode_operations; + inode->i_fop =3D &ssdfs_file_operations; + inode->i_mapping->a_ops =3D &ssdfs_aops; + } else if (S_ISDIR(inode->i_mode)) { + inode->i_op =3D &ssdfs_dir_inode_operations; + inode->i_fop =3D &ssdfs_dir_operations; + inode->i_mapping->a_ops =3D &ssdfs_aops; + } else if (S_ISLNK(inode->i_mode)) { + inode->i_op =3D &ssdfs_symlink_inode_operations; + inode->i_mapping->a_ops =3D &ssdfs_aops; + inode_nohighmem(inode); + } else if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode) || + S_ISFIFO(inode->i_mode) || S_ISSOCK(inode->i_mode)) { + inode->i_op =3D &ssdfs_special_inode_operations; + init_special_inode(inode, inode->i_mode, inode->i_rdev); + } else { + SSDFS_ERR("bogus i_mode %o for ino %lu\n", + inode->i_mode, (unsigned long)inode->i_ino); + return -EINVAL; + } + + return 0; +} + +static int ssdfs_read_inode(struct inode *inode) +{ + struct ssdfs_fs_info *fsi =3D SSDFS_FS_I(inode->i_sb); + struct ssdfs_btree_search *search; + struct ssdfs_inode *raw_inode; + size_t raw_inode_size; + struct ssdfs_inode_info *ii =3D SSDFS_I(inode); + u16 private_flags; + int err =3D 0; + +#ifdef CONFIG_SSDFS_DEBUG + SSDFS_DBG("ino %lu\n", (unsigned long)inode->i_ino); +#endif /* CONFIG_SSDFS_DEBUG */ + + search =3D ssdfs_btree_search_alloc(); + if (!search) { + SSDFS_ERR("fail to allocate btree search object\n"); + return -ENOMEM; + } + + ssdfs_btree_search_init(search); + + err =3D ssdfs_inodes_btree_find(fsi->inodes_tree, inode->i_ino, search); + if (unlikely(err)) { + SSDFS_ERR("fail to find the raw inode: " + "ino %lu, err %d\n", + inode->i_ino, err); + goto finish_read_inode; + } + + switch (search->result.state) { + case SSDFS_BTREE_SEARCH_VALID_ITEM: + /* expected state */ + break; + + default: + err =3D -ERANGE; + SSDFS_ERR("invalid result state %#x\n", + search->result.state); + goto finish_read_inode; + } + + switch (search->result.raw_buf.state) { + case SSDFS_BTREE_SEARCH_INLINE_BUFFER: + case SSDFS_BTREE_SEARCH_EXTERNAL_BUFFER: + /* expected state */ + break; + + default: + err =3D -ERANGE; + SSDFS_ERR("invalid buffer state %#x\n", + search->result.raw_buf.state); + goto finish_read_inode; + } + + if (!search->result.raw_buf.place.ptr) { + err =3D -ERANGE; + SSDFS_ERR("empty result buffer pointer\n"); + goto finish_read_inode; + } + + if (search->result.raw_buf.items_count =3D=3D 0) { + err =3D -ERANGE; + SSDFS_ERR("items_in_buffer %u\n", + search->result.raw_buf.items_count); + goto finish_read_inode; + } + + raw_inode =3D (struct ssdfs_inode *)search->result.raw_buf.place.ptr; + raw_inode_size =3D + search->result.raw_buf.size / search->result.raw_buf.items_count; + + if (!is_raw_inode_checksum_correct(fsi, raw_inode, raw_inode_size)) { + err =3D -EIO; + SSDFS_ERR("invalid inode's checksum: ino %lu\n", + inode->i_ino); + goto finish_read_inode; + } + + if (le16_to_cpu(raw_inode->magic) !=3D SSDFS_INODE_MAGIC) { + err =3D -EIO; + SSDFS_ERR("invalid inode magic %#x\n", + le16_to_cpu(raw_inode->magic)); + goto finish_read_inode; + } + + if (le64_to_cpu(raw_inode->ino) !=3D inode->i_ino) { + err =3D -EIO; + SSDFS_ERR("raw_inode->ino %llu !=3D i_ino %lu\n", + le64_to_cpu(raw_inode->ino), + inode->i_ino); + goto finish_read_inode; + } + + inode->i_mode =3D le16_to_cpu(raw_inode->mode); + ii->flags =3D le32_to_cpu(raw_inode->flags); + i_uid_write(inode, le32_to_cpu(raw_inode->uid)); + i_gid_write(inode, le32_to_cpu(raw_inode->gid)); + set_nlink(inode, le32_to_cpu(raw_inode->refcount)); + + inode_set_atime(inode, le64_to_cpu(raw_inode->atime), + le32_to_cpu(raw_inode->atime_nsec)); + inode_set_ctime(inode, le64_to_cpu(raw_inode->ctime), + le32_to_cpu(raw_inode->ctime_nsec)); + inode_set_mtime(inode, le64_to_cpu(raw_inode->mtime), + le32_to_cpu(raw_inode->mtime_nsec)); + + ii->birthtime.tv_sec =3D le64_to_cpu(raw_inode->birthtime); + ii->birthtime.tv_nsec =3D le32_to_cpu(raw_inode->birthtime_nsec); + ii->raw_inode_size =3D fsi->raw_inode_size; + + inode->i_generation =3D (u32)le64_to_cpu(raw_inode->generation); + + inode->i_size =3D le64_to_cpu(raw_inode->size); + inode->i_blkbits =3D fsi->log_pagesize; + inode->i_blocks =3D le64_to_cpu(raw_inode->blocks); + + private_flags =3D le16_to_cpu(raw_inode->private_flags); + atomic_set(&ii->private_flags, private_flags); + if (private_flags & ~SSDFS_INODE_PRIVATE_FLAGS_MASK) { + err =3D -EIO; + SSDFS_ERR("invalid set of private_flags %#x\n", + private_flags); + goto finish_read_inode; + } + + if (fsi->pagesize > SSDFS_8KB) + mapping_set_large_folios(inode->i_mapping); + + err =3D ssdfs_inode_setops(inode); + if (unlikely(err)) + goto finish_read_inode; + + down_write(&ii->lock); + + ii->parent_ino =3D le64_to_cpu(raw_inode->parent_ino); + ssdfs_set_inode_flags(inode); + ii->name_hash =3D le64_to_cpu(raw_inode->hash_code); + ii->name_len =3D le16_to_cpu(raw_inode->name_len); + + ssdfs_memcpy(&ii->raw_inode, + 0, sizeof(struct ssdfs_inode), + raw_inode, + 0, sizeof(struct ssdfs_inode), + sizeof(struct ssdfs_inode)); + + if (S_ISREG(inode->i_mode)) { + if (private_flags & ~SSDFS_IFREG_PRIVATE_FLAG_MASK) { + err =3D -EIO; + SSDFS_ERR("regular file: invalid private flags %#x\n", + private_flags); + goto unlock_mutable_fields; + } + + if (is_ssdfs_file_inline(ii)) { + err =3D ssdfs_allocate_inline_file_buffer(inode); + if (unlikely(err)) { + SSDFS_ERR("fail to allocate inline buffer\n"); + goto unlock_mutable_fields; + } + + /* + * TODO: pre-fetch file's content in buffer + * (if inode size > 256 bytes) + */ + } else if (private_flags & SSDFS_INODE_HAS_INLINE_EXTENTS || + private_flags & SSDFS_INODE_HAS_EXTENTS_BTREE) { + err =3D ssdfs_extents_tree_create(fsi, ii); + if (unlikely(err)) { + SSDFS_ERR("fail to create the extents tree: " + "ino %lu, err %d\n", + inode->i_ino, err); + goto unlock_mutable_fields; + } + + err =3D ssdfs_extents_tree_init(fsi, ii); + if (unlikely(err)) { + SSDFS_ERR("fail to init the extents tree: " + "ino %lu, err %d\n", + inode->i_ino, err); + goto unlock_mutable_fields; + } + } + + if (private_flags & SSDFS_INODE_HAS_INLINE_XATTR || + private_flags & SSDFS_INODE_HAS_XATTR_BTREE) { + err =3D ssdfs_xattrs_tree_create(fsi, ii); + if (unlikely(err)) { + SSDFS_ERR("fail to create the xattrs tree: " + "ino %lu, err %d\n", + inode->i_ino, err); + goto unlock_mutable_fields; + } + + err =3D ssdfs_xattrs_tree_init(fsi, ii); + if (unlikely(err)) { + SSDFS_ERR("fail to init the xattrs tree: " + "ino %lu, err %d\n", + inode->i_ino, err); + goto unlock_mutable_fields; + } + } + } else if (S_ISDIR(inode->i_mode)) { + if (private_flags & ~SSDFS_IFDIR_PRIVATE_FLAG_MASK) { + err =3D -EIO; + SSDFS_ERR("folder: invalid private flags %#x\n", + private_flags); + goto unlock_mutable_fields; + } + + if (private_flags & SSDFS_INODE_HAS_INLINE_DENTRIES || + private_flags & SSDFS_INODE_HAS_DENTRIES_BTREE) { + err =3D ssdfs_dentries_tree_create(fsi, ii); + if (unlikely(err)) { + SSDFS_ERR("fail to create the dentries tree: " + "ino %lu, err %d\n", + inode->i_ino, err); + goto unlock_mutable_fields; + } + + err =3D ssdfs_dentries_tree_init(fsi, ii); + if (unlikely(err)) { + SSDFS_ERR("fail to init the dentries tree: " + "ino %lu, err %d\n", + inode->i_ino, err); + goto unlock_mutable_fields; + } + } + + if (private_flags & SSDFS_INODE_HAS_INLINE_XATTR || + private_flags & SSDFS_INODE_HAS_XATTR_BTREE) { + err =3D ssdfs_xattrs_tree_create(fsi, ii); + if (unlikely(err)) { + SSDFS_ERR("fail to create the xattrs tree: " + "ino %lu, err %d\n", + inode->i_ino, err); + goto unlock_mutable_fields; + } + + err =3D ssdfs_xattrs_tree_init(fsi, ii); + if (unlikely(err)) { + SSDFS_ERR("fail to init the xattrs tree: " + "ino %lu, err %d\n", + inode->i_ino, err); + goto unlock_mutable_fields; + } + } + } else if (S_ISLNK(inode->i_mode) || + S_ISCHR(inode->i_mode) || + S_ISBLK(inode->i_mode) || + S_ISFIFO(inode->i_mode) || + S_ISSOCK(inode->i_mode)) { + /* do nothing */ + } else { + err =3D -EINVAL; + SSDFS_ERR("bogus i_mode %o for ino %lu\n", + inode->i_mode, (unsigned long)inode->i_ino); + goto unlock_mutable_fields; + } + +unlock_mutable_fields: + up_write(&ii->lock); + +finish_read_inode: + ssdfs_btree_search_free(search); + return err; +} + +struct inode *ssdfs_iget(struct super_block *sb, ino_t ino) +{ + struct inode *inode; + int err; + +#ifdef CONFIG_SSDFS_DEBUG + SSDFS_DBG("ino %lu\n", (unsigned long)ino); +#endif /* CONFIG_SSDFS_DEBUG */ + + inode =3D iget_locked(sb, ino); + if (unlikely(!inode)) { + err =3D -ENOMEM; + SSDFS_ERR("unable to obtain or to allocate inode %lu, err %d\n", + (unsigned long)ino, err); + return ERR_PTR(err); + } + + if (!(inode_state_read_once(inode) & I_NEW)) { + trace_ssdfs_iget(inode); + return inode; + } + + err =3D ssdfs_read_inode(inode); + if (unlikely(err)) { + SSDFS_ERR("unable to read inode %lu, err %d\n", + (unsigned long)ino, err); + goto bad_inode; + } + + unlock_new_inode(inode); + trace_ssdfs_iget(inode); + return inode; + +bad_inode: + iget_failed(inode); + trace_ssdfs_iget_exit(inode, err); + return ERR_PTR(err); +} + +static void ssdfs_init_raw_inode(struct ssdfs_inode_info *ii) +{ + struct ssdfs_inode *ri =3D &ii->raw_inode; + + ri->magic =3D cpu_to_le16(SSDFS_INODE_MAGIC); + ri->mode =3D cpu_to_le16(ii->vfs_inode.i_mode); + ri->flags =3D cpu_to_le32(ii->flags); + ri->uid =3D cpu_to_le32(i_uid_read(&ii->vfs_inode)); + ri->gid =3D cpu_to_le32(i_gid_read(&ii->vfs_inode)); + ri->atime =3D cpu_to_le64(inode_get_atime_sec(&ii->vfs_inode)); + ri->ctime =3D cpu_to_le64(inode_get_ctime_sec(&ii->vfs_inode)); + ri->mtime =3D cpu_to_le64(inode_get_mtime_sec(&ii->vfs_inode)); + ri->atime_nsec =3D cpu_to_le32(inode_get_atime_nsec(&ii->vfs_inode)); + ri->ctime_nsec =3D cpu_to_le32(inode_get_ctime_nsec(&ii->vfs_inode)); + ri->mtime_nsec =3D cpu_to_le32(inode_get_mtime_nsec(&ii->vfs_inode)); + ri->birthtime =3D cpu_to_le64(ii->birthtime.tv_sec); + ri->birthtime_nsec =3D cpu_to_le32(ii->birthtime.tv_nsec); + ri->generation =3D cpu_to_le64(ii->vfs_inode.i_generation); + ri->size =3D cpu_to_le64(i_size_read(&ii->vfs_inode)); + ri->blocks =3D cpu_to_le64(ii->vfs_inode.i_blocks); + ri->parent_ino =3D cpu_to_le64(ii->parent_ino); + ri->refcount =3D cpu_to_le32(ii->vfs_inode.i_nlink); + ri->checksum =3D 0; + ri->ino =3D cpu_to_le64(ii->vfs_inode.i_ino); + ri->hash_code =3D cpu_to_le64(ii->name_hash); + ri->name_len =3D cpu_to_le16(ii->name_len); +} + +static void ssdfs_init_inode(struct mnt_idmap *idmap, + struct inode *dir, + struct inode *inode, + umode_t mode, + ino_t ino, + const struct qstr *qstr) +{ + struct super_block *sb =3D dir->i_sb; + struct ssdfs_fs_info *fsi =3D SSDFS_FS_I(sb); + struct ssdfs_inode_info *ii =3D SSDFS_I(inode); + + inode->i_ino =3D ino; + ii->parent_ino =3D dir->i_ino; + ii->birthtime =3D current_time(inode); + ii->raw_inode_size =3D fsi->raw_inode_size; + inode_set_atime_to_ts(inode, ii->birthtime); + inode_set_mtime_to_ts(inode, ii->birthtime); + inode_set_ctime_to_ts(inode, ii->birthtime); + inode_init_owner(idmap, inode, dir, mode); + ii->flags =3D ssdfs_mask_flags(mode, + SSDFS_I(dir)->flags & SSDFS_FL_INHERITED); + ssdfs_set_inode_flags(inode); + inode->i_generation =3D get_random_u32(); + inode->i_blkbits =3D fsi->log_pagesize; + i_size_write(inode, 0); + inode->i_blocks =3D 0; + set_nlink(inode, 1); + + down_write(&ii->lock); + ii->name_hash =3D ssdfs_generate_name_hash(qstr); + ii->name_len =3D (u16)qstr->len; + ssdfs_init_raw_inode(ii); + up_write(&ii->lock); + + if (fsi->pagesize > SSDFS_8KB) + mapping_set_large_folios(inode->i_mapping); +} + +struct inode *ssdfs_new_inode(struct mnt_idmap *idmap, + struct inode *dir, umode_t mode, + const struct qstr *qstr) +{ + struct ssdfs_fs_info *fsi =3D SSDFS_FS_I(dir->i_sb); + struct super_block *sb =3D dir->i_sb; + struct inode *inode; + struct ssdfs_btree_search *search; + struct ssdfs_inodes_btree_info *itree; + ino_t ino; + int err =3D 0; + +#ifdef CONFIG_SSDFS_DEBUG + SSDFS_DBG("dir_ino %lu, mode %o\n", + (unsigned long)dir->i_ino, mode); +#endif /* CONFIG_SSDFS_DEBUG */ + + itree =3D fsi->inodes_tree; + + search =3D ssdfs_btree_search_alloc(); + if (!search) { + err =3D -ENOMEM; + SSDFS_ERR("fail to allocate btree search object\n"); + goto failed_new_inode; + } + + ssdfs_btree_search_init(search); + err =3D ssdfs_inodes_btree_allocate(itree, &ino, search); + ssdfs_btree_search_free(search); + + if (err =3D=3D -ENOSPC) { +#ifdef CONFIG_SSDFS_DEBUG + SSDFS_DBG("unable to allocate an inode: " + "dir_ino %lu, err %d\n", + (unsigned long)dir->i_ino, err); +#endif /* CONFIG_SSDFS_DEBUG */ + goto failed_new_inode; + } else if (unlikely(err)) { + SSDFS_ERR("fail to allocate an inode: " + "dir_ino %lu, err %d\n", + (unsigned long)dir->i_ino, err); + goto failed_new_inode; + } + + inode =3D new_inode(sb); + if (unlikely(!inode)) { + err =3D -ENOMEM; + SSDFS_ERR("unable to allocate inode: err %d\n", err); + goto failed_new_inode; + } + + ssdfs_init_inode(idmap, dir, inode, mode, ino, qstr); + + err =3D ssdfs_inode_setops(inode); + if (unlikely(err)) { + SSDFS_ERR("fail to set inode's operations: " + "err %d\n", err); + goto bad_inode; + } + + if (insert_inode_locked(inode) < 0) { + err =3D -EIO; + SSDFS_ERR("inode number already in use: " + "ino %lu\n", + (unsigned long) ino); + goto bad_inode; + } + + err =3D ssdfs_init_acl(inode, dir); + if (err) { + SSDFS_ERR("fail to init ACL: " + "err %d\n", err); + goto fail_drop; + } + + err =3D ssdfs_init_security(inode, dir, qstr); + if (err) { + SSDFS_ERR("fail to init security xattr: " + "err %d\n", err); + goto fail_drop; + } + + mark_inode_dirty(inode); + +#ifdef CONFIG_SSDFS_DEBUG + SSDFS_DBG("new inode %lu is created\n", + ino); +#endif /* CONFIG_SSDFS_DEBUG */ + + trace_ssdfs_inode_new(inode); + return inode; + +fail_drop: + trace_ssdfs_inode_new_exit(inode, err); + clear_nlink(inode); + unlock_new_inode(inode); + iput(inode); + return ERR_PTR(err); + +bad_inode: + trace_ssdfs_inode_new_exit(inode, err); + make_bad_inode(inode); + iput(inode); + +failed_new_inode: + return ERR_PTR(err); +} + +int ssdfs_getattr(struct mnt_idmap *idmap, + const struct path *path, struct kstat *stat, + u32 request_mask, unsigned int query_flags) +{ + struct inode *inode =3D d_inode(path->dentry); + struct ssdfs_inode_info *ii =3D SSDFS_I(inode); + unsigned int flags; + +#ifdef CONFIG_SSDFS_DEBUG + SSDFS_DBG("ino %lu\n", (unsigned long)inode->i_ino); +#endif /* CONFIG_SSDFS_DEBUG */ + + flags =3D ii->flags & SSDFS_FL_USER_VISIBLE; + if (flags & SSDFS_APPEND_FL) + stat->attributes |=3D STATX_ATTR_APPEND; + if (flags & SSDFS_COMPR_FL) + stat->attributes |=3D STATX_ATTR_COMPRESSED; + if (flags & SSDFS_IMMUTABLE_FL) + stat->attributes |=3D STATX_ATTR_IMMUTABLE; + if (flags & SSDFS_NODUMP_FL) + stat->attributes |=3D STATX_ATTR_NODUMP; + + stat->attributes_mask |=3D (STATX_ATTR_APPEND | + STATX_ATTR_COMPRESSED | + STATX_ATTR_ENCRYPTED | + STATX_ATTR_IMMUTABLE | + STATX_ATTR_NODUMP); + + generic_fillattr(idmap, request_mask, inode, stat); + return 0; +} + +static int ssdfs_truncate(struct inode *inode) +{ + struct ssdfs_fs_info *fsi =3D SSDFS_FS_I(inode->i_sb); + struct ssdfs_inode_info *ii =3D SSDFS_I(inode); + struct ssdfs_extents_btree_info *tree =3D NULL; + struct timespec64 cur_time; + int err; + +#ifdef CONFIG_SSDFS_DEBUG + SSDFS_DBG("ino %lu\n", (unsigned long)inode->i_ino); +#endif /* CONFIG_SSDFS_DEBUG */ + + if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || + S_ISLNK(inode->i_mode))) + return -EINVAL; + + if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) + return -EPERM; + + if (is_ssdfs_file_inline(ii)) { + loff_t newsize =3D i_size_read(inode); + size_t inline_capacity =3D + ssdfs_inode_inline_file_capacity(inode); + + if (newsize > inline_capacity) { + SSDFS_ERR("newsize %llu > inline_capacity %zu\n", + (u64)newsize, inline_capacity); + return -E2BIG; + } else if (newsize =3D=3D inline_capacity) { + /* do nothing */ +#ifdef CONFIG_SSDFS_DEBUG + SSDFS_DBG("newsize %llu =3D=3D inline_capacity %zu\n", + (u64)newsize, inline_capacity); +#endif /* CONFIG_SSDFS_DEBUG */ + } else if (newsize > 0) { + loff_t size =3D inline_capacity - newsize; + +#ifdef CONFIG_SSDFS_DEBUG + BUG_ON(!ii->inline_file); +#endif /* CONFIG_SSDFS_DEBUG */ + + memset((u8 *)ii->inline_file + newsize, 0, size); + } + } else { + tree =3D SSDFS_EXTREE(ii); + if (!tree) { +#ifdef CONFIG_SSDFS_DEBUG + SSDFS_DBG("extents tree is absent: ino %lu\n", + ii->vfs_inode.i_ino); +#endif /* CONFIG_SSDFS_DEBUG */ + return -ENOENT; + } + + switch (atomic_read(&tree->state)) { + case SSDFS_EXTENTS_BTREE_DIRTY: + down_write(&ii->lock); + err =3D ssdfs_extents_tree_flush(fsi, ii); + up_write(&ii->lock); + + if (unlikely(err)) { + SSDFS_ERR("fail to flush extents tree: " + "ino %lu, err %d\n", + inode->i_ino, err); + return err; + } + break; + + default: + /* do nothing */ + break; + } + + err =3D ssdfs_extents_tree_truncate(inode); + if (unlikely(err)) { + SSDFS_ERR("fail to truncate extents tree: " + "err %d\n", + err); + return err; + } + } + + cur_time =3D current_time(inode); + inode_set_mtime_to_ts(inode, cur_time); + inode_set_ctime_to_ts(inode, cur_time); + + mark_inode_dirty(inode); + + return 0; +} + +static int ssdfs_setsize(struct inode *inode, struct iattr *attr) +{ + loff_t oldsize =3D i_size_read(inode); + loff_t newsize =3D attr->ia_size; + struct timespec64 cur_time; + int err =3D 0; + +#ifdef CONFIG_SSDFS_DEBUG + SSDFS_DBG("ino %lu\n", (unsigned long)inode->i_ino); +#endif /* CONFIG_SSDFS_DEBUG */ + + if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || + S_ISLNK(inode->i_mode))) + return -EINVAL; + + if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) + return -EPERM; + + inode_dio_wait(inode); + + if (newsize > oldsize) { + i_size_write(inode, newsize); + pagecache_isize_extended(inode, oldsize, newsize); + } else { + truncate_setsize(inode, newsize); + + err =3D ssdfs_truncate(inode); + if (err) + return err; + } + + cur_time =3D current_time(inode); + inode_set_mtime_to_ts(inode, cur_time); + inode_set_ctime_to_ts(inode, cur_time); + + mark_inode_dirty(inode); + return 0; +} + +int ssdfs_setattr(struct mnt_idmap *idmap, + struct dentry *dentry, struct iattr *attr) +{ + struct inode *inode =3D dentry->d_inode; + int err =3D 0; + +#ifdef CONFIG_SSDFS_DEBUG + SSDFS_DBG("ino %lu\n", (unsigned long)inode->i_ino); +#endif /* CONFIG_SSDFS_DEBUG */ + + err =3D setattr_prepare(idmap, dentry, attr); + if (err) + return err; + + if (S_ISREG(inode->i_mode) && + attr->ia_valid & ATTR_SIZE && + attr->ia_size !=3D inode->i_size) { + err =3D ssdfs_setsize(inode, attr); + if (err) + return err; + } + + if (attr->ia_valid) { + setattr_copy(idmap, inode, attr); + mark_inode_dirty(inode); + + if (attr->ia_valid & ATTR_MODE) { + err =3D posix_acl_chmod(idmap, + dentry, inode->i_mode); + } + } + + return err; +} + +/* + * This method does all fs work to be done when in-core inode + * is about to be gone, for whatever reason. + */ +void ssdfs_evict_inode(struct inode *inode) +{ + struct ssdfs_fs_info *fsi =3D SSDFS_FS_I(inode->i_sb); + struct ssdfs_xattrs_btree_info *xattrs_tree; + ino_t ino =3D inode->i_ino; + bool want_delete =3D false; + int err; + +#ifdef CONFIG_SSDFS_DEBUG + SSDFS_DBG("ino %lu mode %o count %d nlink %u\n", + ino, inode->i_mode, + atomic_read(&inode->i_count), + inode->i_nlink); +#endif /* CONFIG_SSDFS_DEBUG */ + + xattrs_tree =3D SSDFS_XATTREE(SSDFS_I(inode)); + + if (!inode->i_nlink) { + err =3D filemap_flush(inode->i_mapping); + if (err) { + SSDFS_WARN("inode %lu flush error: %d\n", + ino, err); + } + } + + err =3D filemap_fdatawait(inode->i_mapping); + if (err) { + SSDFS_WARN("inode %lu fdatawait error: %d\n", + ino, err); + ssdfs_clear_dirty_folios(inode->i_mapping); + } + + if (!inode->i_nlink && !is_bad_inode(inode)) + want_delete =3D true; + else + want_delete =3D false; + +#ifdef CONFIG_SSDFS_DEBUG + SSDFS_DBG("ino %lu mode %o count %d nlink %u, " + "is_bad_inode %#x, want_delete %#x\n", + ino, inode->i_mode, + atomic_read(&inode->i_count), + inode->i_nlink, + is_bad_inode(inode), + want_delete); +#endif /* CONFIG_SSDFS_DEBUG */ + + trace_ssdfs_inode_evict(inode); + + truncate_inode_pages_final(&inode->i_data); + + if (want_delete) { + sb_start_intwrite(inode->i_sb); + + i_size_write(inode, 0); + + err =3D ssdfs_truncate(inode); + if (err =3D=3D -ENOENT) { + err =3D 0; +#ifdef CONFIG_SSDFS_DEBUG + SSDFS_DBG("extents tree is absent: ino %lu\n", + ino); +#endif /* CONFIG_SSDFS_DEBUG */ + } else if (err) { + SSDFS_WARN("fail to truncate inode: " + "ino %lu, err %d\n", + ino, err); + } + + if (xattrs_tree) { + err =3D ssdfs_xattrs_tree_delete_all(xattrs_tree); + if (err) { + SSDFS_WARN("fail to truncate xattrs tree: " + "ino %lu, err %d\n", + ino, err); + } + } + } + + clear_inode(inode); + + if (want_delete) { + err =3D ssdfs_inodes_btree_delete(fsi->inodes_tree, ino); + if (err) { + SSDFS_WARN("fail to deallocate raw inode: " + "ino %lu, err %d\n", + ino, err); + } + + sb_end_intwrite(inode->i_sb); + } +} + +/* + * This method is called when the VFS needs to write an + * inode to disc + */ +int ssdfs_write_inode(struct inode *inode, struct writeback_control *wbc) +{ + struct ssdfs_fs_info *fsi =3D SSDFS_FS_I(inode->i_sb); + struct ssdfs_inode_info *ii =3D SSDFS_I(inode); + struct ssdfs_inode *ri =3D &ii->raw_inode; + struct ssdfs_inodes_btree_info *itree; + struct ssdfs_btree_search *search; + struct ssdfs_dentries_btree_descriptor dentries_btree; + bool has_save_dentries_desc =3D true; + size_t dentries_desc_size =3D + sizeof(struct ssdfs_dentries_btree_descriptor); + struct ssdfs_extents_btree_descriptor extents_btree; + bool has_save_extents_desc =3D true; + size_t extents_desc_size =3D + sizeof(struct ssdfs_extents_btree_descriptor); + struct ssdfs_xattr_btree_descriptor xattr_btree; + bool has_save_xattrs_desc =3D true; + size_t xattr_desc_size =3D + sizeof(struct ssdfs_xattr_btree_descriptor); + int private_flags; + size_t raw_inode_size; + ino_t ino; + int err =3D 0; + +#ifdef CONFIG_SSDFS_DEBUG + SSDFS_DBG("ino %lu\n", (unsigned long)inode->i_ino); +#endif /* CONFIG_SSDFS_DEBUG */ + + down_read(&fsi->volume_sem); + raw_inode_size =3D le16_to_cpu(fsi->vs->inodes_btree.desc.item_size); + ssdfs_memcpy(&dentries_btree, 0, dentries_desc_size, + &fsi->vh->dentries_btree, 0, dentries_desc_size, + dentries_desc_size); + ssdfs_memcpy(&extents_btree, 0, extents_desc_size, + &fsi->vh->extents_btree, 0, extents_desc_size, + extents_desc_size); + ssdfs_memcpy(&xattr_btree, 0, xattr_desc_size, + &fsi->vh->xattr_btree, 0, xattr_desc_size, + xattr_desc_size); + up_read(&fsi->volume_sem); + + if (raw_inode_size !=3D sizeof(struct ssdfs_inode)) { + SSDFS_WARN("raw_inode_size %zu !=3D size %zu\n", + raw_inode_size, + sizeof(struct ssdfs_inode)); + return -ERANGE; + } + + itree =3D fsi->inodes_tree; + ino =3D inode->i_ino; + + search =3D ssdfs_btree_search_alloc(); + if (!search) { + SSDFS_ERR("fail to allocate btree search object\n"); + return -ENOMEM; + } + + ssdfs_btree_search_init(search); + + err =3D ssdfs_inodes_btree_find(itree, ino, search); + if (unlikely(err)) { +#ifdef CONFIG_SSDFS_TESTING + err =3D 0; + SSDFS_DBG("fail to find inode: " + "ino %lu, err %d\n", + ino, err); +#else + SSDFS_ERR("fail to find inode: " + "ino %lu, err %d\n", + ino, err); +#endif /* CONFIG_SSDFS_TESTING */ + goto free_search_object; + } + + down_write(&ii->lock); + + ssdfs_init_raw_inode(ii); + + if (S_ISREG(inode->i_mode) && ii->extents_tree) { + err =3D ssdfs_extents_tree_flush(fsi, ii); + if (unlikely(err)) { + SSDFS_ERR("fail to flush extents tree: " + "ino %lu, err %d\n", + inode->i_ino, err); + goto finish_write_inode; + } + + if (memcmp(&extents_btree, &ii->extents_tree->desc, + extents_desc_size) !=3D 0) { + ssdfs_memcpy(&extents_btree, + 0, extents_desc_size, + &ii->extents_tree->desc, + 0, extents_desc_size, + extents_desc_size); + has_save_extents_desc =3D true; + } else + has_save_extents_desc =3D false; + } else if (S_ISDIR(inode->i_mode) && ii->dentries_tree) { + err =3D ssdfs_dentries_tree_flush(fsi, ii); + if (unlikely(err)) { + SSDFS_ERR("fail to flush dentries tree: " + "ino %lu, err %d\n", + inode->i_ino, err); + goto finish_write_inode; + } + + if (memcmp(&dentries_btree, &ii->dentries_tree->desc, + dentries_desc_size) !=3D 0) { + ssdfs_memcpy(&dentries_btree, + 0, dentries_desc_size, + &ii->dentries_tree->desc, + 0, dentries_desc_size, + dentries_desc_size); + has_save_dentries_desc =3D true; + } else + has_save_dentries_desc =3D false; + } + + if (ii->xattrs_tree) { + err =3D ssdfs_xattrs_tree_flush(fsi, ii); + if (unlikely(err)) { + SSDFS_ERR("fail to flush xattrs tree: " + "ino %lu, err %d\n", + inode->i_ino, err); + goto finish_write_inode; + } + + if (memcmp(&xattr_btree, &ii->xattrs_tree->desc, + xattr_desc_size) !=3D 0) { + ssdfs_memcpy(&xattr_btree, + 0, xattr_desc_size, + &ii->xattrs_tree->desc, + 0, xattr_desc_size, + xattr_desc_size); + has_save_xattrs_desc =3D true; + } else + has_save_xattrs_desc =3D false; + } + + private_flags =3D atomic_read(&ii->private_flags); + if (private_flags & ~SSDFS_INODE_PRIVATE_FLAGS_MASK) { + err =3D -ERANGE; + SSDFS_WARN("invalid set of private_flags %#x\n", + private_flags); + goto finish_write_inode; + } else + ri->private_flags =3D cpu_to_le16((u16)private_flags); + + ri->checksum =3D ssdfs_crc32_le(ri, raw_inode_size); + + switch (search->result.raw_buf.state) { + case SSDFS_BTREE_SEARCH_INLINE_BUFFER: + case SSDFS_BTREE_SEARCH_EXTERNAL_BUFFER: + /* expected state */ + break; + + default: + err =3D -ERANGE; + SSDFS_ERR("invalid result's buffer state: " + "%#x\n", + search->result.raw_buf.state); + goto finish_write_inode; + } + + if (!search->result.raw_buf.place.ptr) { + err =3D -ERANGE; + SSDFS_ERR("invalid buffer\n"); + goto finish_write_inode; + } + + if (search->result.raw_buf.size < raw_inode_size) { + err =3D -ERANGE; + SSDFS_ERR("buf_size %zu < raw_inode_size %zu\n", + search->result.raw_buf.size, + raw_inode_size); + goto finish_write_inode; + } + + if (search->result.raw_buf.items_count !=3D 1) { + SSDFS_WARN("unexpected value: " + "items_in_buffer %u\n", + search->result.raw_buf.items_count); + } + + ssdfs_memcpy(search->result.raw_buf.place.ptr, + 0, search->result.raw_buf.size, + ri, + 0, raw_inode_size, + raw_inode_size); + +finish_write_inode: + up_write(&ii->lock); + + if (unlikely(err)) + goto free_search_object; + + if (has_save_dentries_desc || has_save_extents_desc || + has_save_xattrs_desc) { + down_write(&fsi->volume_sem); + ssdfs_memcpy(&fsi->vh->dentries_btree, + 0, dentries_desc_size, + &dentries_btree, + 0, dentries_desc_size, + dentries_desc_size); + ssdfs_memcpy(&fsi->vh->extents_btree, + 0, extents_desc_size, + &extents_btree, + 0, extents_desc_size, + extents_desc_size); + ssdfs_memcpy(&fsi->vh->xattr_btree, + 0, xattr_desc_size, + &xattr_btree, + 0, xattr_desc_size, + xattr_desc_size); + up_write(&fsi->volume_sem); + } + + err =3D ssdfs_inodes_btree_change(itree, ino, search); + if (unlikely(err)) { + SSDFS_ERR("fail to change inode: " + "ino %lu, err %d\n", + ino, err); + goto free_search_object; + } + +free_search_object: + ssdfs_btree_search_free(search); + +#ifdef CONFIG_SSDFS_DEBUG + SSDFS_DBG("finished: ino %lu, err %d\n", + (unsigned long)inode->i_ino, err); +#endif /* CONFIG_SSDFS_DEBUG */ + + return err; +} + +/* + * This method is called when the VFS needs + * to get filesystem statistics + */ +int ssdfs_statfs(struct dentry *dentry, struct kstatfs *buf) +{ + struct super_block *sb =3D dentry->d_sb; + struct ssdfs_fs_info *fsi =3D SSDFS_FS_I(sb); +#ifdef CONFIG_SSDFS_BLOCK_DEVICE + u64 id =3D huge_encode_dev(sb->s_bdev->bd_dev); +#endif + u64 nsegs; + u32 pages_per_seg; + +#ifdef CONFIG_SSDFS_DEBUG + SSDFS_DBG("ino %lu\n", (unsigned long)dentry->d_inode->i_ino); +#endif /* CONFIG_SSDFS_DEBUG */ + + buf->f_type =3D SSDFS_SUPER_MAGIC; + buf->f_bsize =3D sb->s_blocksize; + buf->f_frsize =3D buf->f_bsize; + + mutex_lock(&fsi->resize_mutex); + nsegs =3D fsi->nsegs; + mutex_unlock(&fsi->resize_mutex); + + pages_per_seg =3D fsi->pages_per_seg; + buf->f_blocks =3D nsegs * pages_per_seg; + + spin_lock(&fsi->volume_state_lock); + buf->f_bfree =3D fsi->free_pages; + spin_unlock(&fsi->volume_state_lock); + + buf->f_bavail =3D buf->f_bfree; + + spin_lock(&fsi->inodes_tree->lock); + buf->f_files =3D fsi->inodes_tree->allocated_inodes; + buf->f_ffree =3D fsi->inodes_tree->free_inodes; + spin_unlock(&fsi->inodes_tree->lock); + + buf->f_namelen =3D SSDFS_MAX_NAME_LEN; + +#ifdef CONFIG_SSDFS_MTD_DEVICE + buf->f_fsid.val[0] =3D SSDFS_SUPER_MAGIC; + buf->f_fsid.val[1] =3D fsi->mtd->index; +#elif defined(CONFIG_SSDFS_BLOCK_DEVICE) + buf->f_fsid.val[0] =3D (u32)id; + buf->f_fsid.val[1] =3D (u32)(id >> 32); +#else + BUILD_BUG(); +#endif + + return 0; +} --=20 2.34.1