aboutsummaryrefslogtreecommitdiffstats
path: root/fs/xfs/xfs_attr_leaf.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/xfs/xfs_attr_leaf.c')
-rw-r--r--fs/xfs/xfs_attr_leaf.c177
1 files changed, 152 insertions, 25 deletions
diff --git a/fs/xfs/xfs_attr_leaf.c b/fs/xfs/xfs_attr_leaf.c
index e13eaa521436..50598b121683 100644
--- a/fs/xfs/xfs_attr_leaf.c
+++ b/fs/xfs/xfs_attr_leaf.c
@@ -118,13 +118,82 @@ STATIC int xfs_attr_put_listent(xfs_attr_list_context_t *context,
118 118
119 119
120/*======================================================================== 120/*========================================================================
121 * External routines when dirsize < XFS_LITINO(mp). 121 * External routines when attribute fork size < XFS_LITINO(mp).
122 *========================================================================*/ 122 *========================================================================*/
123 123
124/* 124/*
125 * Create the initial contents of a shortform attribute list. 125 * Query whether the requested number of additional bytes of extended
126 * attribute space will be able to fit inline.
127 * Returns zero if not, else the di_forkoff fork offset to be used in the
128 * literal area for attribute data once the new bytes have been added.
129 *
130 * di_forkoff must be 8 byte aligned, hence is stored as a >>3 value;
131 * special case for dev/uuid inodes, they have fixed size data forks.
126 */ 132 */
127int 133int
134xfs_attr_shortform_bytesfit(xfs_inode_t *dp, int bytes)
135{
136 int offset;
137 int minforkoff; /* lower limit on valid forkoff locations */
138 int maxforkoff; /* upper limit on valid forkoff locations */
139 xfs_mount_t *mp = dp->i_mount;
140
141 if (unlikely(mp->m_flags & XFS_MOUNT_COMPAT_ATTR)) {
142 if (bytes <= XFS_IFORK_ASIZE(dp))
143 return mp->m_attroffset >> 3;
144 return 0;
145 }
146
147 offset = (XFS_LITINO(mp) - bytes) >> 3; /* rounded down */
148
149 switch (dp->i_d.di_format) {
150 case XFS_DINODE_FMT_DEV:
151 minforkoff = roundup(sizeof(xfs_dev_t), 8) >> 3;
152 return (offset >= minforkoff) ? minforkoff : 0;
153 case XFS_DINODE_FMT_UUID:
154 minforkoff = roundup(sizeof(uuid_t), 8) >> 3;
155 return (offset >= minforkoff) ? minforkoff : 0;
156 }
157
158 /* data fork btree root can have at least this many key/ptr pairs */
159 minforkoff = MAX(dp->i_df.if_bytes, XFS_BMDR_SPACE_CALC(MINDBTPTRS));
160 minforkoff = roundup(minforkoff, 8) >> 3;
161
162 /* attr fork btree root can have at least this many key/ptr pairs */
163 maxforkoff = XFS_LITINO(mp) - XFS_BMDR_SPACE_CALC(MINABTPTRS);
164 maxforkoff = maxforkoff >> 3; /* rounded down */
165
166 if (offset >= minforkoff && offset < maxforkoff)
167 return offset;
168 if (offset >= maxforkoff)
169 return maxforkoff;
170 return 0;
171}
172
173/*
174 * Switch on the ATTR2 superblock bit (implies also FEATURES2)
175 */
176STATIC void
177xfs_sbversion_add_attr2(xfs_mount_t *mp, xfs_trans_t *tp)
178{
179 unsigned long s;
180
181 if (!(mp->m_flags & XFS_MOUNT_COMPAT_ATTR) &&
182 !(XFS_SB_VERSION_HASATTR2(&mp->m_sb))) {
183 s = XFS_SB_LOCK(mp);
184 if (!XFS_SB_VERSION_HASATTR2(&mp->m_sb)) {
185 XFS_SB_VERSION_ADDATTR2(&mp->m_sb);
186 XFS_SB_UNLOCK(mp, s);
187 xfs_mod_sb(tp, XFS_SB_VERSIONNUM | XFS_SB_FEATURES2);
188 } else
189 XFS_SB_UNLOCK(mp, s);
190 }
191}
192
193/*
194 * Create the initial contents of a shortform attribute list.
195 */
196void
128xfs_attr_shortform_create(xfs_da_args_t *args) 197xfs_attr_shortform_create(xfs_da_args_t *args)
129{ 198{
130 xfs_attr_sf_hdr_t *hdr; 199 xfs_attr_sf_hdr_t *hdr;
@@ -148,29 +217,37 @@ xfs_attr_shortform_create(xfs_da_args_t *args)
148 hdr->count = 0; 217 hdr->count = 0;
149 INT_SET(hdr->totsize, ARCH_CONVERT, sizeof(*hdr)); 218 INT_SET(hdr->totsize, ARCH_CONVERT, sizeof(*hdr));
150 xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE | XFS_ILOG_ADATA); 219 xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE | XFS_ILOG_ADATA);
151 return(0);
152} 220}
153 221
154/* 222/*
155 * Add a name/value pair to the shortform attribute list. 223 * Add a name/value pair to the shortform attribute list.
156 * Overflow from the inode has already been checked for. 224 * Overflow from the inode has already been checked for.
157 */ 225 */
158int 226void
159xfs_attr_shortform_add(xfs_da_args_t *args) 227xfs_attr_shortform_add(xfs_da_args_t *args, int forkoff)
160{ 228{
161 xfs_attr_shortform_t *sf; 229 xfs_attr_shortform_t *sf;
162 xfs_attr_sf_entry_t *sfe; 230 xfs_attr_sf_entry_t *sfe;
163 int i, offset, size; 231 int i, offset, size;
232 xfs_mount_t *mp;
164 xfs_inode_t *dp; 233 xfs_inode_t *dp;
165 xfs_ifork_t *ifp; 234 xfs_ifork_t *ifp;
166 235
167 dp = args->dp; 236 dp = args->dp;
237 mp = dp->i_mount;
238 dp->i_d.di_forkoff = forkoff;
239 dp->i_df.if_ext_max =
240 XFS_IFORK_DSIZE(dp) / (uint)sizeof(xfs_bmbt_rec_t);
241 dp->i_afp->if_ext_max =
242 XFS_IFORK_ASIZE(dp) / (uint)sizeof(xfs_bmbt_rec_t);
243
168 ifp = dp->i_afp; 244 ifp = dp->i_afp;
169 ASSERT(ifp->if_flags & XFS_IFINLINE); 245 ASSERT(ifp->if_flags & XFS_IFINLINE);
170 sf = (xfs_attr_shortform_t *)ifp->if_u1.if_data; 246 sf = (xfs_attr_shortform_t *)ifp->if_u1.if_data;
171 sfe = &sf->list[0]; 247 sfe = &sf->list[0];
172 for (i = 0; i < INT_GET(sf->hdr.count, ARCH_CONVERT); 248 for (i = 0; i < INT_GET(sf->hdr.count, ARCH_CONVERT);
173 sfe = XFS_ATTR_SF_NEXTENTRY(sfe), i++) { 249 sfe = XFS_ATTR_SF_NEXTENTRY(sfe), i++) {
250#ifdef DEBUG
174 if (sfe->namelen != args->namelen) 251 if (sfe->namelen != args->namelen)
175 continue; 252 continue;
176 if (memcmp(args->name, sfe->nameval, args->namelen) != 0) 253 if (memcmp(args->name, sfe->nameval, args->namelen) != 0)
@@ -181,7 +258,8 @@ xfs_attr_shortform_add(xfs_da_args_t *args)
181 if (((args->flags & ATTR_ROOT) != 0) != 258 if (((args->flags & ATTR_ROOT) != 0) !=
182 ((sfe->flags & XFS_ATTR_ROOT) != 0)) 259 ((sfe->flags & XFS_ATTR_ROOT) != 0))
183 continue; 260 continue;
184 return(XFS_ERROR(EEXIST)); 261 ASSERT(0);
262#endif
185 } 263 }
186 264
187 offset = (char *)sfe - (char *)sf; 265 offset = (char *)sfe - (char *)sf;
@@ -200,11 +278,11 @@ xfs_attr_shortform_add(xfs_da_args_t *args)
200 INT_MOD(sf->hdr.totsize, ARCH_CONVERT, size); 278 INT_MOD(sf->hdr.totsize, ARCH_CONVERT, size);
201 xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE | XFS_ILOG_ADATA); 279 xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE | XFS_ILOG_ADATA);
202 280
203 return(0); 281 xfs_sbversion_add_attr2(mp, args->trans);
204} 282}
205 283
206/* 284/*
207 * Remove a name from the shortform attribute list structure. 285 * Remove an attribute from the shortform attribute list structure.
208 */ 286 */
209int 287int
210xfs_attr_shortform_remove(xfs_da_args_t *args) 288xfs_attr_shortform_remove(xfs_da_args_t *args)
@@ -212,17 +290,16 @@ xfs_attr_shortform_remove(xfs_da_args_t *args)
212 xfs_attr_shortform_t *sf; 290 xfs_attr_shortform_t *sf;
213 xfs_attr_sf_entry_t *sfe; 291 xfs_attr_sf_entry_t *sfe;
214 int base, size=0, end, totsize, i; 292 int base, size=0, end, totsize, i;
293 xfs_mount_t *mp;
215 xfs_inode_t *dp; 294 xfs_inode_t *dp;
216 295
217 /*
218 * Remove the attribute.
219 */
220 dp = args->dp; 296 dp = args->dp;
297 mp = dp->i_mount;
221 base = sizeof(xfs_attr_sf_hdr_t); 298 base = sizeof(xfs_attr_sf_hdr_t);
222 sf = (xfs_attr_shortform_t *)dp->i_afp->if_u1.if_data; 299 sf = (xfs_attr_shortform_t *)dp->i_afp->if_u1.if_data;
223 sfe = &sf->list[0]; 300 sfe = &sf->list[0];
224 for (i = 0; i < INT_GET(sf->hdr.count, ARCH_CONVERT); 301 end = INT_GET(sf->hdr.count, ARCH_CONVERT);
225 sfe = XFS_ATTR_SF_NEXTENTRY(sfe), 302 for (i = 0; i < end; sfe = XFS_ATTR_SF_NEXTENTRY(sfe),
226 base += size, i++) { 303 base += size, i++) {
227 size = XFS_ATTR_SF_ENTSIZE(sfe); 304 size = XFS_ATTR_SF_ENTSIZE(sfe);
228 if (sfe->namelen != args->namelen) 305 if (sfe->namelen != args->namelen)
@@ -237,19 +314,51 @@ xfs_attr_shortform_remove(xfs_da_args_t *args)
237 continue; 314 continue;
238 break; 315 break;
239 } 316 }
240 if (i == INT_GET(sf->hdr.count, ARCH_CONVERT)) 317 if (i == end)
241 return(XFS_ERROR(ENOATTR)); 318 return(XFS_ERROR(ENOATTR));
242 319
320 /*
321 * Fix up the attribute fork data, covering the hole
322 */
243 end = base + size; 323 end = base + size;
244 totsize = INT_GET(sf->hdr.totsize, ARCH_CONVERT); 324 totsize = INT_GET(sf->hdr.totsize, ARCH_CONVERT);
245 if (end != totsize) { 325 if (end != totsize)
246 memmove(&((char *)sf)[base], &((char *)sf)[end], 326 memmove(&((char *)sf)[base], &((char *)sf)[end], totsize - end);
247 totsize - end);
248 }
249 INT_MOD(sf->hdr.count, ARCH_CONVERT, -1); 327 INT_MOD(sf->hdr.count, ARCH_CONVERT, -1);
250 INT_MOD(sf->hdr.totsize, ARCH_CONVERT, -size); 328 INT_MOD(sf->hdr.totsize, ARCH_CONVERT, -size);
251 xfs_idata_realloc(dp, -size, XFS_ATTR_FORK); 329
252 xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE | XFS_ILOG_ADATA); 330 /*
331 * Fix up the start offset of the attribute fork
332 */
333 totsize -= size;
334 if (totsize == sizeof(xfs_attr_sf_hdr_t) && !args->addname) {
335 /*
336 * Last attribute now removed, revert to original
337 * inode format making all literal area available
338 * to the data fork once more.
339 */
340 xfs_idestroy_fork(dp, XFS_ATTR_FORK);
341 dp->i_d.di_forkoff = 0;
342 dp->i_d.di_aformat = XFS_DINODE_FMT_EXTENTS;
343 ASSERT(dp->i_d.di_anextents == 0);
344 ASSERT(dp->i_afp == NULL);
345 dp->i_df.if_ext_max =
346 XFS_IFORK_DSIZE(dp) / (uint)sizeof(xfs_bmbt_rec_t);
347 xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE);
348 } else {
349 xfs_idata_realloc(dp, -size, XFS_ATTR_FORK);
350 dp->i_d.di_forkoff = xfs_attr_shortform_bytesfit(dp, totsize);
351 ASSERT(dp->i_d.di_forkoff);
352 ASSERT(totsize > sizeof(xfs_attr_sf_hdr_t) || args->addname);
353 dp->i_afp->if_ext_max =
354 XFS_IFORK_ASIZE(dp) / (uint)sizeof(xfs_bmbt_rec_t);
355 dp->i_df.if_ext_max =
356 XFS_IFORK_DSIZE(dp) / (uint)sizeof(xfs_bmbt_rec_t);
357 xfs_trans_log_inode(args->trans, dp,
358 XFS_ILOG_CORE | XFS_ILOG_ADATA);
359 }
360
361 xfs_sbversion_add_attr2(mp, args->trans);
253 362
254 return(0); 363 return(0);
255} 364}
@@ -649,14 +758,16 @@ xfs_attr_shortform_allfit(xfs_dabuf_t *bp, xfs_inode_t *dp)
649 + name_loc->namelen 758 + name_loc->namelen
650 + INT_GET(name_loc->valuelen, ARCH_CONVERT); 759 + INT_GET(name_loc->valuelen, ARCH_CONVERT);
651 } 760 }
652 return( bytes < XFS_IFORK_ASIZE(dp) ); 761 if (bytes == sizeof(struct xfs_attr_sf_hdr))
762 return(-1);
763 return(xfs_attr_shortform_bytesfit(dp, bytes));
653} 764}
654 765
655/* 766/*
656 * Convert a leaf attribute list to shortform attribute list 767 * Convert a leaf attribute list to shortform attribute list
657 */ 768 */
658int 769int
659xfs_attr_leaf_to_shortform(xfs_dabuf_t *bp, xfs_da_args_t *args) 770xfs_attr_leaf_to_shortform(xfs_dabuf_t *bp, xfs_da_args_t *args, int forkoff)
660{ 771{
661 xfs_attr_leafblock_t *leaf; 772 xfs_attr_leafblock_t *leaf;
662 xfs_attr_leaf_entry_t *entry; 773 xfs_attr_leaf_entry_t *entry;
@@ -683,9 +794,25 @@ xfs_attr_leaf_to_shortform(xfs_dabuf_t *bp, xfs_da_args_t *args)
683 error = xfs_da_shrink_inode(args, 0, bp); 794 error = xfs_da_shrink_inode(args, 0, bp);
684 if (error) 795 if (error)
685 goto out; 796 goto out;
686 error = xfs_attr_shortform_create(args); 797
687 if (error) 798 if (forkoff == -1) {
799 /*
800 * Last attribute was removed, revert to original
801 * inode format making all literal area available
802 * to the data fork once more.
803 */
804 xfs_idestroy_fork(dp, XFS_ATTR_FORK);
805 dp->i_d.di_forkoff = 0;
806 dp->i_d.di_aformat = XFS_DINODE_FMT_EXTENTS;
807 ASSERT(dp->i_d.di_anextents == 0);
808 ASSERT(dp->i_afp == NULL);
809 dp->i_df.if_ext_max =
810 XFS_IFORK_DSIZE(dp) / (uint)sizeof(xfs_bmbt_rec_t);
811 xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE);
688 goto out; 812 goto out;
813 }
814
815 xfs_attr_shortform_create(args);
689 816
690 /* 817 /*
691 * Copy the attributes 818 * Copy the attributes
@@ -713,7 +840,7 @@ xfs_attr_leaf_to_shortform(xfs_dabuf_t *bp, xfs_da_args_t *args)
713 nargs.hashval = INT_GET(entry->hashval, ARCH_CONVERT); 840 nargs.hashval = INT_GET(entry->hashval, ARCH_CONVERT);
714 nargs.flags = (entry->flags & XFS_ATTR_SECURE) ? ATTR_SECURE : 841 nargs.flags = (entry->flags & XFS_ATTR_SECURE) ? ATTR_SECURE :
715 ((entry->flags & XFS_ATTR_ROOT) ? ATTR_ROOT : 0); 842 ((entry->flags & XFS_ATTR_ROOT) ? ATTR_ROOT : 0);
716 xfs_attr_shortform_add(&nargs); 843 xfs_attr_shortform_add(&nargs, forkoff);
717 } 844 }
718 error = 0; 845 error = 0;
719 846