1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Copyright (c) 2000,2005 Silicon Graphics, Inc.
4 * All Rights Reserved.
5 */
6 #include "xfs_platform.h"
7 #include "xfs_fs.h"
8 #include "xfs_shared.h"
9 #include "xfs_format.h"
10 #include "xfs_da_format.h"
11 #include "xfs_log_format.h"
12 #include "xfs_trans_resv.h"
13 #include "xfs_mount.h"
14 #include "xfs_da_btree.h"
15 #include "xfs_bmap_btree.h"
16 #include "xfs_trans_space.h"
17
18 /* Calculate the disk space required to add a parent pointer. */
19 unsigned int
xfs_parent_calc_space_res(struct xfs_mount * mp,unsigned int namelen)20 xfs_parent_calc_space_res(
21 struct xfs_mount *mp,
22 unsigned int namelen)
23 {
24 /*
25 * A parent pointer is recorded per dirent, so an inode with N links
26 * carries N of them and the attr fork can already be in leaf or node
27 * format when one is added. That does not affect the reservation:
28 * XFS_DAENTER_SPACE_RES covers a split at every level of a
29 * maximum-depth attr dabtree, whatever format the fork is in now.
30 *
31 * The name is a dirent name and the value is a struct xfs_parent_rec,
32 * so the leaf entry is always local and never exceeds 272 bytes.
33 * Parent pointers require V5, hence a 1k minimum block size, so the
34 * entry always stays under half a block and this needs none of the
35 * double split allowance that xfs_attr_calc_size() makes.
36 *
37 * The second term hands a byte count to a macro whose parameter counts
38 * mappings, so it asks for more extent-add allowance than the single
39 * mapping a parent pointer adds - how much more depends on the block
40 * size. It over-reserves either way, which is why it is left alone:
41 * correcting the unit would shrink a reservation that is only generous.
42 */
43 return XFS_DAENTER_SPACE_RES(mp, XFS_ATTR_FORK) +
44 XFS_NEXTENTADD_SPACE_RES(mp, namelen, XFS_ATTR_FORK);
45 }
46
47 unsigned int
xfs_create_space_res(struct xfs_mount * mp,unsigned int namelen)48 xfs_create_space_res(
49 struct xfs_mount *mp,
50 unsigned int namelen)
51 {
52 unsigned int ret;
53
54 ret = XFS_IALLOC_SPACE_RES(mp) + XFS_DIRENTER_SPACE_RES(mp, namelen);
55 if (xfs_has_parent(mp))
56 ret += xfs_parent_calc_space_res(mp, namelen);
57
58 return ret;
59 }
60
61 unsigned int
xfs_mkdir_space_res(struct xfs_mount * mp,unsigned int namelen)62 xfs_mkdir_space_res(
63 struct xfs_mount *mp,
64 unsigned int namelen)
65 {
66 return xfs_create_space_res(mp, namelen);
67 }
68
69 unsigned int
xfs_link_space_res(struct xfs_mount * mp,unsigned int namelen)70 xfs_link_space_res(
71 struct xfs_mount *mp,
72 unsigned int namelen)
73 {
74 unsigned int ret;
75
76 ret = XFS_DIRENTER_SPACE_RES(mp, namelen);
77 if (xfs_has_parent(mp))
78 ret += xfs_parent_calc_space_res(mp, namelen);
79
80 return ret;
81 }
82
83 unsigned int
xfs_symlink_space_res(struct xfs_mount * mp,unsigned int namelen,unsigned int fsblocks)84 xfs_symlink_space_res(
85 struct xfs_mount *mp,
86 unsigned int namelen,
87 unsigned int fsblocks)
88 {
89 unsigned int ret;
90
91 ret = XFS_IALLOC_SPACE_RES(mp) + XFS_DIRENTER_SPACE_RES(mp, namelen) +
92 fsblocks;
93
94 if (xfs_has_parent(mp))
95 ret += xfs_parent_calc_space_res(mp, namelen);
96
97 return ret;
98 }
99
100 unsigned int
xfs_remove_space_res(struct xfs_mount * mp,unsigned int namelen)101 xfs_remove_space_res(
102 struct xfs_mount *mp,
103 unsigned int namelen)
104 {
105 unsigned int ret = XFS_DIRREMOVE_SPACE_RES(mp);
106
107 if (xfs_has_parent(mp))
108 ret += xfs_parent_calc_space_res(mp, namelen);
109
110 return ret;
111 }
112
113 unsigned int
xfs_rename_space_res(struct xfs_mount * mp,unsigned int src_namelen,bool target_exists,unsigned int target_namelen,bool has_whiteout)114 xfs_rename_space_res(
115 struct xfs_mount *mp,
116 unsigned int src_namelen,
117 bool target_exists,
118 unsigned int target_namelen,
119 bool has_whiteout)
120 {
121 unsigned int ret;
122
123 ret = XFS_DIRREMOVE_SPACE_RES(mp) +
124 XFS_DIRENTER_SPACE_RES(mp, target_namelen);
125
126 if (xfs_has_parent(mp)) {
127 if (has_whiteout)
128 ret += xfs_parent_calc_space_res(mp, src_namelen);
129 ret += 2 * xfs_parent_calc_space_res(mp, target_namelen);
130 }
131
132 if (target_exists)
133 ret += xfs_parent_calc_space_res(mp, target_namelen);
134
135 return ret;
136 }
137