if (value == NULL)
value_len = 0; if (name == NULL) return -EINVAL;
name_len = strlen(name); if (name_len > 255 || value_len > sb->s_blocksize) return -ERANGE;
error = dquot_initialize(inode); if (error) return error;
down_write(&EXT2_I(inode)->xattr_sem); if (EXT2_I(inode)->i_file_acl) { /* The inode already has an extended attribute block. */
bh = sb_bread(sb, EXT2_I(inode)->i_file_acl);
error = -EIO; if (!bh) goto cleanup;
ea_bdebug(bh, "b_count=%d, refcount=%d",
atomic_read(&(bh->b_count)),
le32_to_cpu(HDR(bh)->h_refcount));
header = HDR(bh);
end = bh->b_data + bh->b_size; if (!ext2_xattr_header_valid(header)) {
bad_block:
ext2_error(sb, "ext2_xattr_set", "inode %ld: bad block %d", inode->i_ino,
EXT2_I(inode)->i_file_acl);
error = -EIO; goto cleanup;
} /* *Findthenamedattribute.Ifnotfound,'here'willpoint *toentrywherethenewattributeshouldbeinsertedto *maintainsorting.
*/
last = FIRST_ENTRY(bh); while (!IS_LAST_ENTRY(last)) { if (!ext2_xattr_entry_valid(last, end, sb->s_blocksize)) goto bad_block; if (last->e_value_size) {
size_t offs = le16_to_cpu(last->e_value_offs); if (offs < min_offs)
min_offs = offs;
} if (not_found > 0) {
not_found = ext2_xattr_cmp_entry(name_index,
name_len,
name, last); if (not_found <= 0)
here = last;
}
last = EXT2_XATTR_NEXT(last);
} if (not_found > 0)
here = last;
/* Check whether we have enough space left. */
free = min_offs - ((char*)last - (char*)header) - sizeof(__u32);
} else { /* We will use a new extended attribute block. */
free = sb->s_blocksize - sizeof(struct ext2_xattr_header) - sizeof(__u32);
}
if (not_found) { /* Request to remove a nonexistent attribute? */
error = -ENODATA; if (flags & XATTR_REPLACE) goto cleanup;
error = 0; if (value == NULL) goto cleanup;
} else { /* Request to create an existing attribute? */
error = -EEXIST; if (flags & XATTR_CREATE) goto cleanup;
free += EXT2_XATTR_SIZE(le32_to_cpu(here->e_value_size));
free += EXT2_XATTR_LEN(name_len);
}
error = -ENOSPC; if (free < EXT2_XATTR_LEN(name_len) + EXT2_XATTR_SIZE(value_len)) goto cleanup;
/* Here we know that we can set the new attribute. */
if (size == EXT2_XATTR_SIZE(value_len)) { /* The old and the new value have the same
size. Just replace. */
here->e_value_size = cpu_to_le32(value_len);
memset(val + size - EXT2_XATTR_PAD, 0,
EXT2_XATTR_PAD); /* Clear pad bytes. */
memcpy(val, value, value_len); goto skip_replace;
}
/* Remove the old value. */
memmove(first_val + size, first_val, val - first_val);
memset(first_val, 0, size);
min_offs += size;
/* Adjust all value offsets. */
last = ENTRY(header+1); while (!IS_LAST_ENTRY(last)) {
size_t o = le16_to_cpu(last->e_value_offs); if (o < offs)
last->e_value_offs =
cpu_to_le16(o + size);
last = EXT2_XATTR_NEXT(last);
}
here->e_value_offs = 0;
} if (value == NULL) { /* Remove the old name. */
size_t size = EXT2_XATTR_LEN(name_len);
last = ENTRY((char *)last - size);
memmove(here, (char*)here + size,
(char*)last - (char*)here);
memset(last, 0, size);
}
}
if (value != NULL) { /* Insert the new value. */
here->e_value_size = cpu_to_le32(value_len); if (value_len) {
size_t size = EXT2_XATTR_SIZE(value_len); char *val = (char *)header + min_offs - size;
here->e_value_offs =
cpu_to_le16((char *)val - (char *)header);
memset(val + size - EXT2_XATTR_PAD, 0,
EXT2_XATTR_PAD); /* Clear the pad bytes. */
memcpy(val, value, value_len);
}
}
skip_replace: if (IS_LAST_ENTRY(ENTRY(header+1))) { /* This block is now empty. */ if (bh && header == HDR(bh))
unlock_buffer(bh); /* we were modifying in-place. */
error = ext2_xattr_set2(inode, bh, NULL);
} else {
ext2_xattr_rehash(header, here); if (bh && header == HDR(bh))
unlock_buffer(bh); /* we were modifying in-place. */
error = ext2_xattr_set2(inode, bh, header);
}
cleanup: if (!(bh && header == HDR(bh)))
kfree(header);
brelse(bh);
up_write(&EXT2_I(inode)->xattr_sem);
/* Free the old block. */
ea_bdebug(bh, "freeing");
ext2_free_blocks(inode, bh->b_blocknr, 1); /* We let our caller release bh, so we
* need to duplicate the buffer before. */
get_bh(bh);
bforget(bh);
unlock_buffer(bh);
} else { /* Decrement the refcount only. */
le32_add_cpu(&HDR(bh)->h_refcount, -1);
dquot_free_block(inode, 1);
mark_buffer_dirty(bh);
unlock_buffer(bh);
ea_bdebug(bh, "refcount now=%d",
le32_to_cpu(HDR(bh)->h_refcount)); if (IS_SYNC(inode))
sync_dirty_buffer(bh);
}
}
if (header) {
new_bh = ext2_xattr_cache_find(inode, header); if (new_bh) { /* We found an identical block in the cache. */ if (new_bh == old_bh) {
ea_bdebug(new_bh, "keeping this block");
} else { /* The old block is released after updating
the inode. */
ea_bdebug(new_bh, "reusing block");
error = dquot_alloc_block(inode, 1); if (error) {
unlock_buffer(new_bh); goto cleanup;
}
le32_add_cpu(&HDR(new_bh)->h_refcount, 1);
ea_bdebug(new_bh, "refcount now=%d",
le32_to_cpu(HDR(new_bh)->h_refcount));
}
unlock_buffer(new_bh);
} elseif (old_bh && header == HDR(old_bh)) { /* Keep this block. No need to lock the block as we
don't need to change the reference count. */
new_bh = old_bh;
get_bh(new_bh);
ext2_xattr_cache_insert(ea_block_cache, new_bh);
} else { /* We need to allocate a new block */
ext2_fsblk_t goal = ext2_group_first_block_no(sb,
EXT2_I(inode)->i_block_group); unsignedlong count = 1;
ext2_fsblk_t block = ext2_new_blocks(inode, goal,
&count, &error,
EXT2_ALLOC_NORESERVE); if (error) goto cleanup;
ea_idebug(inode, "creating block %lu", block);
ext2_xattr_update_super_block(sb);
}
mark_buffer_dirty(new_bh); if (IS_SYNC(inode)) {
sync_dirty_buffer(new_bh);
error = -EIO; if (buffer_req(new_bh) && !buffer_uptodate(new_bh)) goto cleanup;
}
}
/* Update the inode. */
EXT2_I(inode)->i_file_acl = new_bh ? new_bh->b_blocknr : 0;
inode_set_ctime_current(inode); if (IS_SYNC(inode)) {
error = sync_inode_metadata(inode, 1); /* In case sync failed due to ENOSPC the inode was actually *written(onlysomedirtydatawerenot)sowejustproceed
* as if nothing happened and cleanup the unused block */ if (error && error != -ENOSPC) { if (new_bh && new_bh != old_bh) {
dquot_free_block_nodirty(inode, 1);
mark_inode_dirty(inode);
} goto cleanup;
}
} else
mark_inode_dirty(inode);
/* *Wearetheonlyonesholdinginodereference.Thexattr_semshould *betterbeunlocked!Wecouldaswelljustnotacquirexattr_semat *allbutthismakesthecodemorefutureproof.OTOHweneedtrylock *heretoavoidfalse-positivewarningfromlockdepaboutreclaim *circulardependency.
*/ if (WARN_ON_ONCE(!down_write_trylock(&EXT2_I(inode)->xattr_sem))) return; if (!EXT2_I(inode)->i_file_acl) goto cleanup;
if (!ext2_data_block_valid(sbi, EXT2_I(inode)->i_file_acl, 1)) {
ext2_error(inode->i_sb, "ext2_xattr_delete_inode", "inode %ld: xattr block %d is out of data blocks range",
inode->i_ino, EXT2_I(inode)->i_file_acl); goto cleanup;
}
void ext2_xattr_destroy_cache(struct mb_cache *cache)
{ if (cache)
mb_cache_destroy(cache);
}
Messung V0.5 in Prozent
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.25Bemerkung:
(vorverarbeitet am 2026-09-28)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.