xref: /OK3568_Linux_fs/kernel/fs/afs/dir_edit.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /* AFS filesystem directory editing
3*4882a593Smuzhiyun  *
4*4882a593Smuzhiyun  * Copyright (C) 2018 Red Hat, Inc. All Rights Reserved.
5*4882a593Smuzhiyun  * Written by David Howells (dhowells@redhat.com)
6*4882a593Smuzhiyun  */
7*4882a593Smuzhiyun 
8*4882a593Smuzhiyun #include <linux/kernel.h>
9*4882a593Smuzhiyun #include <linux/fs.h>
10*4882a593Smuzhiyun #include <linux/namei.h>
11*4882a593Smuzhiyun #include <linux/pagemap.h>
12*4882a593Smuzhiyun #include <linux/iversion.h>
13*4882a593Smuzhiyun #include "internal.h"
14*4882a593Smuzhiyun #include "xdr_fs.h"
15*4882a593Smuzhiyun 
16*4882a593Smuzhiyun /*
17*4882a593Smuzhiyun  * Find a number of contiguous clear bits in a directory block bitmask.
18*4882a593Smuzhiyun  *
19*4882a593Smuzhiyun  * There are 64 slots, which means we can load the entire bitmap into a
20*4882a593Smuzhiyun  * variable.  The first bit doesn't count as it corresponds to the block header
21*4882a593Smuzhiyun  * slot.  nr_slots is between 1 and 9.
22*4882a593Smuzhiyun  */
afs_find_contig_bits(union afs_xdr_dir_block * block,unsigned int nr_slots)23*4882a593Smuzhiyun static int afs_find_contig_bits(union afs_xdr_dir_block *block, unsigned int nr_slots)
24*4882a593Smuzhiyun {
25*4882a593Smuzhiyun 	u64 bitmap;
26*4882a593Smuzhiyun 	u32 mask;
27*4882a593Smuzhiyun 	int bit, n;
28*4882a593Smuzhiyun 
29*4882a593Smuzhiyun 	bitmap  = (u64)block->hdr.bitmap[0] << 0 * 8;
30*4882a593Smuzhiyun 	bitmap |= (u64)block->hdr.bitmap[1] << 1 * 8;
31*4882a593Smuzhiyun 	bitmap |= (u64)block->hdr.bitmap[2] << 2 * 8;
32*4882a593Smuzhiyun 	bitmap |= (u64)block->hdr.bitmap[3] << 3 * 8;
33*4882a593Smuzhiyun 	bitmap |= (u64)block->hdr.bitmap[4] << 4 * 8;
34*4882a593Smuzhiyun 	bitmap |= (u64)block->hdr.bitmap[5] << 5 * 8;
35*4882a593Smuzhiyun 	bitmap |= (u64)block->hdr.bitmap[6] << 6 * 8;
36*4882a593Smuzhiyun 	bitmap |= (u64)block->hdr.bitmap[7] << 7 * 8;
37*4882a593Smuzhiyun 	bitmap >>= 1; /* The first entry is metadata */
38*4882a593Smuzhiyun 	bit = 1;
39*4882a593Smuzhiyun 	mask = (1 << nr_slots) - 1;
40*4882a593Smuzhiyun 
41*4882a593Smuzhiyun 	do {
42*4882a593Smuzhiyun 		if (sizeof(unsigned long) == 8)
43*4882a593Smuzhiyun 			n = ffz(bitmap);
44*4882a593Smuzhiyun 		else
45*4882a593Smuzhiyun 			n = ((u32)bitmap) != 0 ?
46*4882a593Smuzhiyun 				ffz((u32)bitmap) :
47*4882a593Smuzhiyun 				ffz((u32)(bitmap >> 32)) + 32;
48*4882a593Smuzhiyun 		bitmap >>= n;
49*4882a593Smuzhiyun 		bit += n;
50*4882a593Smuzhiyun 
51*4882a593Smuzhiyun 		if ((bitmap & mask) == 0) {
52*4882a593Smuzhiyun 			if (bit > 64 - nr_slots)
53*4882a593Smuzhiyun 				return -1;
54*4882a593Smuzhiyun 			return bit;
55*4882a593Smuzhiyun 		}
56*4882a593Smuzhiyun 
57*4882a593Smuzhiyun 		n = __ffs(bitmap);
58*4882a593Smuzhiyun 		bitmap >>= n;
59*4882a593Smuzhiyun 		bit += n;
60*4882a593Smuzhiyun 	} while (bitmap);
61*4882a593Smuzhiyun 
62*4882a593Smuzhiyun 	return -1;
63*4882a593Smuzhiyun }
64*4882a593Smuzhiyun 
65*4882a593Smuzhiyun /*
66*4882a593Smuzhiyun  * Set a number of contiguous bits in the directory block bitmap.
67*4882a593Smuzhiyun  */
afs_set_contig_bits(union afs_xdr_dir_block * block,int bit,unsigned int nr_slots)68*4882a593Smuzhiyun static void afs_set_contig_bits(union afs_xdr_dir_block *block,
69*4882a593Smuzhiyun 				int bit, unsigned int nr_slots)
70*4882a593Smuzhiyun {
71*4882a593Smuzhiyun 	u64 mask;
72*4882a593Smuzhiyun 
73*4882a593Smuzhiyun 	mask = (1 << nr_slots) - 1;
74*4882a593Smuzhiyun 	mask <<= bit;
75*4882a593Smuzhiyun 
76*4882a593Smuzhiyun 	block->hdr.bitmap[0] |= (u8)(mask >> 0 * 8);
77*4882a593Smuzhiyun 	block->hdr.bitmap[1] |= (u8)(mask >> 1 * 8);
78*4882a593Smuzhiyun 	block->hdr.bitmap[2] |= (u8)(mask >> 2 * 8);
79*4882a593Smuzhiyun 	block->hdr.bitmap[3] |= (u8)(mask >> 3 * 8);
80*4882a593Smuzhiyun 	block->hdr.bitmap[4] |= (u8)(mask >> 4 * 8);
81*4882a593Smuzhiyun 	block->hdr.bitmap[5] |= (u8)(mask >> 5 * 8);
82*4882a593Smuzhiyun 	block->hdr.bitmap[6] |= (u8)(mask >> 6 * 8);
83*4882a593Smuzhiyun 	block->hdr.bitmap[7] |= (u8)(mask >> 7 * 8);
84*4882a593Smuzhiyun }
85*4882a593Smuzhiyun 
86*4882a593Smuzhiyun /*
87*4882a593Smuzhiyun  * Clear a number of contiguous bits in the directory block bitmap.
88*4882a593Smuzhiyun  */
afs_clear_contig_bits(union afs_xdr_dir_block * block,int bit,unsigned int nr_slots)89*4882a593Smuzhiyun static void afs_clear_contig_bits(union afs_xdr_dir_block *block,
90*4882a593Smuzhiyun 				  int bit, unsigned int nr_slots)
91*4882a593Smuzhiyun {
92*4882a593Smuzhiyun 	u64 mask;
93*4882a593Smuzhiyun 
94*4882a593Smuzhiyun 	mask = (1 << nr_slots) - 1;
95*4882a593Smuzhiyun 	mask <<= bit;
96*4882a593Smuzhiyun 
97*4882a593Smuzhiyun 	block->hdr.bitmap[0] &= ~(u8)(mask >> 0 * 8);
98*4882a593Smuzhiyun 	block->hdr.bitmap[1] &= ~(u8)(mask >> 1 * 8);
99*4882a593Smuzhiyun 	block->hdr.bitmap[2] &= ~(u8)(mask >> 2 * 8);
100*4882a593Smuzhiyun 	block->hdr.bitmap[3] &= ~(u8)(mask >> 3 * 8);
101*4882a593Smuzhiyun 	block->hdr.bitmap[4] &= ~(u8)(mask >> 4 * 8);
102*4882a593Smuzhiyun 	block->hdr.bitmap[5] &= ~(u8)(mask >> 5 * 8);
103*4882a593Smuzhiyun 	block->hdr.bitmap[6] &= ~(u8)(mask >> 6 * 8);
104*4882a593Smuzhiyun 	block->hdr.bitmap[7] &= ~(u8)(mask >> 7 * 8);
105*4882a593Smuzhiyun }
106*4882a593Smuzhiyun 
107*4882a593Smuzhiyun /*
108*4882a593Smuzhiyun  * Scan a directory block looking for a dirent of the right name.
109*4882a593Smuzhiyun  */
afs_dir_scan_block(union afs_xdr_dir_block * block,struct qstr * name,unsigned int blocknum)110*4882a593Smuzhiyun static int afs_dir_scan_block(union afs_xdr_dir_block *block, struct qstr *name,
111*4882a593Smuzhiyun 			      unsigned int blocknum)
112*4882a593Smuzhiyun {
113*4882a593Smuzhiyun 	union afs_xdr_dirent *de;
114*4882a593Smuzhiyun 	u64 bitmap;
115*4882a593Smuzhiyun 	int d, len, n;
116*4882a593Smuzhiyun 
117*4882a593Smuzhiyun 	_enter("");
118*4882a593Smuzhiyun 
119*4882a593Smuzhiyun 	bitmap  = (u64)block->hdr.bitmap[0] << 0 * 8;
120*4882a593Smuzhiyun 	bitmap |= (u64)block->hdr.bitmap[1] << 1 * 8;
121*4882a593Smuzhiyun 	bitmap |= (u64)block->hdr.bitmap[2] << 2 * 8;
122*4882a593Smuzhiyun 	bitmap |= (u64)block->hdr.bitmap[3] << 3 * 8;
123*4882a593Smuzhiyun 	bitmap |= (u64)block->hdr.bitmap[4] << 4 * 8;
124*4882a593Smuzhiyun 	bitmap |= (u64)block->hdr.bitmap[5] << 5 * 8;
125*4882a593Smuzhiyun 	bitmap |= (u64)block->hdr.bitmap[6] << 6 * 8;
126*4882a593Smuzhiyun 	bitmap |= (u64)block->hdr.bitmap[7] << 7 * 8;
127*4882a593Smuzhiyun 
128*4882a593Smuzhiyun 	for (d = (blocknum == 0 ? AFS_DIR_RESV_BLOCKS0 : AFS_DIR_RESV_BLOCKS);
129*4882a593Smuzhiyun 	     d < AFS_DIR_SLOTS_PER_BLOCK;
130*4882a593Smuzhiyun 	     d++) {
131*4882a593Smuzhiyun 		if (!((bitmap >> d) & 1))
132*4882a593Smuzhiyun 			continue;
133*4882a593Smuzhiyun 		de = &block->dirents[d];
134*4882a593Smuzhiyun 		if (de->u.valid != 1)
135*4882a593Smuzhiyun 			continue;
136*4882a593Smuzhiyun 
137*4882a593Smuzhiyun 		/* The block was NUL-terminated by afs_dir_check_page(). */
138*4882a593Smuzhiyun 		len = strlen(de->u.name);
139*4882a593Smuzhiyun 		if (len == name->len &&
140*4882a593Smuzhiyun 		    memcmp(de->u.name, name->name, name->len) == 0)
141*4882a593Smuzhiyun 			return d;
142*4882a593Smuzhiyun 
143*4882a593Smuzhiyun 		n = round_up(12 + len + 1 + 4, AFS_DIR_DIRENT_SIZE);
144*4882a593Smuzhiyun 		n /= AFS_DIR_DIRENT_SIZE;
145*4882a593Smuzhiyun 		d += n - 1;
146*4882a593Smuzhiyun 	}
147*4882a593Smuzhiyun 
148*4882a593Smuzhiyun 	return -1;
149*4882a593Smuzhiyun }
150*4882a593Smuzhiyun 
151*4882a593Smuzhiyun /*
152*4882a593Smuzhiyun  * Initialise a new directory block.  Note that block 0 is special and contains
153*4882a593Smuzhiyun  * some extra metadata.
154*4882a593Smuzhiyun  */
afs_edit_init_block(union afs_xdr_dir_block * meta,union afs_xdr_dir_block * block,int block_num)155*4882a593Smuzhiyun static void afs_edit_init_block(union afs_xdr_dir_block *meta,
156*4882a593Smuzhiyun 				union afs_xdr_dir_block *block, int block_num)
157*4882a593Smuzhiyun {
158*4882a593Smuzhiyun 	memset(block, 0, sizeof(*block));
159*4882a593Smuzhiyun 	block->hdr.npages = htons(1);
160*4882a593Smuzhiyun 	block->hdr.magic = AFS_DIR_MAGIC;
161*4882a593Smuzhiyun 	block->hdr.bitmap[0] = 1;
162*4882a593Smuzhiyun 
163*4882a593Smuzhiyun 	if (block_num == 0) {
164*4882a593Smuzhiyun 		block->hdr.bitmap[0] = 0xff;
165*4882a593Smuzhiyun 		block->hdr.bitmap[1] = 0x1f;
166*4882a593Smuzhiyun 		memset(block->meta.alloc_ctrs,
167*4882a593Smuzhiyun 		       AFS_DIR_SLOTS_PER_BLOCK,
168*4882a593Smuzhiyun 		       sizeof(block->meta.alloc_ctrs));
169*4882a593Smuzhiyun 		meta->meta.alloc_ctrs[0] =
170*4882a593Smuzhiyun 			AFS_DIR_SLOTS_PER_BLOCK - AFS_DIR_RESV_BLOCKS0;
171*4882a593Smuzhiyun 	}
172*4882a593Smuzhiyun 
173*4882a593Smuzhiyun 	if (block_num < AFS_DIR_BLOCKS_WITH_CTR)
174*4882a593Smuzhiyun 		meta->meta.alloc_ctrs[block_num] =
175*4882a593Smuzhiyun 			AFS_DIR_SLOTS_PER_BLOCK - AFS_DIR_RESV_BLOCKS;
176*4882a593Smuzhiyun }
177*4882a593Smuzhiyun 
178*4882a593Smuzhiyun /*
179*4882a593Smuzhiyun  * Edit a directory's file data to add a new directory entry.  Doing this after
180*4882a593Smuzhiyun  * create, mkdir, symlink, link or rename if the data version number is
181*4882a593Smuzhiyun  * incremented by exactly one avoids the need to re-download the entire
182*4882a593Smuzhiyun  * directory contents.
183*4882a593Smuzhiyun  *
184*4882a593Smuzhiyun  * The caller must hold the inode locked.
185*4882a593Smuzhiyun  */
afs_edit_dir_add(struct afs_vnode * vnode,struct qstr * name,struct afs_fid * new_fid,enum afs_edit_dir_reason why)186*4882a593Smuzhiyun void afs_edit_dir_add(struct afs_vnode *vnode,
187*4882a593Smuzhiyun 		      struct qstr *name, struct afs_fid *new_fid,
188*4882a593Smuzhiyun 		      enum afs_edit_dir_reason why)
189*4882a593Smuzhiyun {
190*4882a593Smuzhiyun 	union afs_xdr_dir_block *meta, *block;
191*4882a593Smuzhiyun 	struct afs_xdr_dir_page *meta_page, *dir_page;
192*4882a593Smuzhiyun 	union afs_xdr_dirent *de;
193*4882a593Smuzhiyun 	struct page *page0, *page;
194*4882a593Smuzhiyun 	unsigned int need_slots, nr_blocks, b;
195*4882a593Smuzhiyun 	pgoff_t index;
196*4882a593Smuzhiyun 	loff_t i_size;
197*4882a593Smuzhiyun 	gfp_t gfp;
198*4882a593Smuzhiyun 	int slot;
199*4882a593Smuzhiyun 
200*4882a593Smuzhiyun 	_enter(",,{%d,%s},", name->len, name->name);
201*4882a593Smuzhiyun 
202*4882a593Smuzhiyun 	i_size = i_size_read(&vnode->vfs_inode);
203*4882a593Smuzhiyun 	if (i_size > AFS_DIR_BLOCK_SIZE * AFS_DIR_MAX_BLOCKS ||
204*4882a593Smuzhiyun 	    (i_size & (AFS_DIR_BLOCK_SIZE - 1))) {
205*4882a593Smuzhiyun 		clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags);
206*4882a593Smuzhiyun 		return;
207*4882a593Smuzhiyun 	}
208*4882a593Smuzhiyun 
209*4882a593Smuzhiyun 	gfp = vnode->vfs_inode.i_mapping->gfp_mask;
210*4882a593Smuzhiyun 	page0 = find_or_create_page(vnode->vfs_inode.i_mapping, 0, gfp);
211*4882a593Smuzhiyun 	if (!page0) {
212*4882a593Smuzhiyun 		clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags);
213*4882a593Smuzhiyun 		_leave(" [fgp]");
214*4882a593Smuzhiyun 		return;
215*4882a593Smuzhiyun 	}
216*4882a593Smuzhiyun 
217*4882a593Smuzhiyun 	/* Work out how many slots we're going to need. */
218*4882a593Smuzhiyun 	need_slots = round_up(12 + name->len + 1 + 4, AFS_DIR_DIRENT_SIZE);
219*4882a593Smuzhiyun 	need_slots /= AFS_DIR_DIRENT_SIZE;
220*4882a593Smuzhiyun 
221*4882a593Smuzhiyun 	meta_page = kmap(page0);
222*4882a593Smuzhiyun 	meta = &meta_page->blocks[0];
223*4882a593Smuzhiyun 	if (i_size == 0)
224*4882a593Smuzhiyun 		goto new_directory;
225*4882a593Smuzhiyun 	nr_blocks = i_size / AFS_DIR_BLOCK_SIZE;
226*4882a593Smuzhiyun 
227*4882a593Smuzhiyun 	/* Find a block that has sufficient slots available.  Each VM page
228*4882a593Smuzhiyun 	 * contains two or more directory blocks.
229*4882a593Smuzhiyun 	 */
230*4882a593Smuzhiyun 	for (b = 0; b < nr_blocks + 1; b++) {
231*4882a593Smuzhiyun 		/* If the directory extended into a new page, then we need to
232*4882a593Smuzhiyun 		 * tack a new page on the end.
233*4882a593Smuzhiyun 		 */
234*4882a593Smuzhiyun 		index = b / AFS_DIR_BLOCKS_PER_PAGE;
235*4882a593Smuzhiyun 		if (index == 0) {
236*4882a593Smuzhiyun 			page = page0;
237*4882a593Smuzhiyun 			dir_page = meta_page;
238*4882a593Smuzhiyun 		} else {
239*4882a593Smuzhiyun 			if (nr_blocks >= AFS_DIR_MAX_BLOCKS)
240*4882a593Smuzhiyun 				goto error;
241*4882a593Smuzhiyun 			gfp = vnode->vfs_inode.i_mapping->gfp_mask;
242*4882a593Smuzhiyun 			page = find_or_create_page(vnode->vfs_inode.i_mapping,
243*4882a593Smuzhiyun 						   index, gfp);
244*4882a593Smuzhiyun 			if (!page)
245*4882a593Smuzhiyun 				goto error;
246*4882a593Smuzhiyun 			if (!PagePrivate(page))
247*4882a593Smuzhiyun 				attach_page_private(page, (void *)1);
248*4882a593Smuzhiyun 			dir_page = kmap(page);
249*4882a593Smuzhiyun 		}
250*4882a593Smuzhiyun 
251*4882a593Smuzhiyun 		/* Abandon the edit if we got a callback break. */
252*4882a593Smuzhiyun 		if (!test_bit(AFS_VNODE_DIR_VALID, &vnode->flags))
253*4882a593Smuzhiyun 			goto invalidated;
254*4882a593Smuzhiyun 
255*4882a593Smuzhiyun 		block = &dir_page->blocks[b % AFS_DIR_BLOCKS_PER_PAGE];
256*4882a593Smuzhiyun 
257*4882a593Smuzhiyun 		_debug("block %u: %2u %3u %u",
258*4882a593Smuzhiyun 		       b,
259*4882a593Smuzhiyun 		       (b < AFS_DIR_BLOCKS_WITH_CTR) ? meta->meta.alloc_ctrs[b] : 99,
260*4882a593Smuzhiyun 		       ntohs(block->hdr.npages),
261*4882a593Smuzhiyun 		       ntohs(block->hdr.magic));
262*4882a593Smuzhiyun 
263*4882a593Smuzhiyun 		/* Initialise the block if necessary. */
264*4882a593Smuzhiyun 		if (b == nr_blocks) {
265*4882a593Smuzhiyun 			_debug("init %u", b);
266*4882a593Smuzhiyun 			afs_edit_init_block(meta, block, b);
267*4882a593Smuzhiyun 			afs_set_i_size(vnode, (b + 1) * AFS_DIR_BLOCK_SIZE);
268*4882a593Smuzhiyun 		}
269*4882a593Smuzhiyun 
270*4882a593Smuzhiyun 		/* Only lower dir pages have a counter in the header. */
271*4882a593Smuzhiyun 		if (b >= AFS_DIR_BLOCKS_WITH_CTR ||
272*4882a593Smuzhiyun 		    meta->meta.alloc_ctrs[b] >= need_slots) {
273*4882a593Smuzhiyun 			/* We need to try and find one or more consecutive
274*4882a593Smuzhiyun 			 * slots to hold the entry.
275*4882a593Smuzhiyun 			 */
276*4882a593Smuzhiyun 			slot = afs_find_contig_bits(block, need_slots);
277*4882a593Smuzhiyun 			if (slot >= 0) {
278*4882a593Smuzhiyun 				_debug("slot %u", slot);
279*4882a593Smuzhiyun 				goto found_space;
280*4882a593Smuzhiyun 			}
281*4882a593Smuzhiyun 		}
282*4882a593Smuzhiyun 
283*4882a593Smuzhiyun 		if (page != page0) {
284*4882a593Smuzhiyun 			unlock_page(page);
285*4882a593Smuzhiyun 			kunmap(page);
286*4882a593Smuzhiyun 			put_page(page);
287*4882a593Smuzhiyun 		}
288*4882a593Smuzhiyun 	}
289*4882a593Smuzhiyun 
290*4882a593Smuzhiyun 	/* There are no spare slots of sufficient size, yet the operation
291*4882a593Smuzhiyun 	 * succeeded.  Download the directory again.
292*4882a593Smuzhiyun 	 */
293*4882a593Smuzhiyun 	trace_afs_edit_dir(vnode, why, afs_edit_dir_create_nospc, 0, 0, 0, 0, name->name);
294*4882a593Smuzhiyun 	clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags);
295*4882a593Smuzhiyun 	goto out_unmap;
296*4882a593Smuzhiyun 
297*4882a593Smuzhiyun new_directory:
298*4882a593Smuzhiyun 	afs_edit_init_block(meta, meta, 0);
299*4882a593Smuzhiyun 	i_size = AFS_DIR_BLOCK_SIZE;
300*4882a593Smuzhiyun 	afs_set_i_size(vnode, i_size);
301*4882a593Smuzhiyun 	slot = AFS_DIR_RESV_BLOCKS0;
302*4882a593Smuzhiyun 	page = page0;
303*4882a593Smuzhiyun 	block = meta;
304*4882a593Smuzhiyun 	nr_blocks = 1;
305*4882a593Smuzhiyun 	b = 0;
306*4882a593Smuzhiyun 
307*4882a593Smuzhiyun found_space:
308*4882a593Smuzhiyun 	/* Set the dirent slot. */
309*4882a593Smuzhiyun 	trace_afs_edit_dir(vnode, why, afs_edit_dir_create, b, slot,
310*4882a593Smuzhiyun 			   new_fid->vnode, new_fid->unique, name->name);
311*4882a593Smuzhiyun 	de = &block->dirents[slot];
312*4882a593Smuzhiyun 	de->u.valid	= 1;
313*4882a593Smuzhiyun 	de->u.unused[0]	= 0;
314*4882a593Smuzhiyun 	de->u.hash_next	= 0; // TODO: Really need to maintain this
315*4882a593Smuzhiyun 	de->u.vnode	= htonl(new_fid->vnode);
316*4882a593Smuzhiyun 	de->u.unique	= htonl(new_fid->unique);
317*4882a593Smuzhiyun 	memcpy(de->u.name, name->name, name->len + 1);
318*4882a593Smuzhiyun 	de->u.name[name->len] = 0;
319*4882a593Smuzhiyun 
320*4882a593Smuzhiyun 	/* Adjust the bitmap. */
321*4882a593Smuzhiyun 	afs_set_contig_bits(block, slot, need_slots);
322*4882a593Smuzhiyun 	if (page != page0) {
323*4882a593Smuzhiyun 		unlock_page(page);
324*4882a593Smuzhiyun 		kunmap(page);
325*4882a593Smuzhiyun 		put_page(page);
326*4882a593Smuzhiyun 	}
327*4882a593Smuzhiyun 
328*4882a593Smuzhiyun 	/* Adjust the allocation counter. */
329*4882a593Smuzhiyun 	if (b < AFS_DIR_BLOCKS_WITH_CTR)
330*4882a593Smuzhiyun 		meta->meta.alloc_ctrs[b] -= need_slots;
331*4882a593Smuzhiyun 
332*4882a593Smuzhiyun 	inode_inc_iversion_raw(&vnode->vfs_inode);
333*4882a593Smuzhiyun 	afs_stat_v(vnode, n_dir_cr);
334*4882a593Smuzhiyun 	_debug("Insert %s in %u[%u]", name->name, b, slot);
335*4882a593Smuzhiyun 
336*4882a593Smuzhiyun out_unmap:
337*4882a593Smuzhiyun 	unlock_page(page0);
338*4882a593Smuzhiyun 	kunmap(page0);
339*4882a593Smuzhiyun 	put_page(page0);
340*4882a593Smuzhiyun 	_leave("");
341*4882a593Smuzhiyun 	return;
342*4882a593Smuzhiyun 
343*4882a593Smuzhiyun invalidated:
344*4882a593Smuzhiyun 	trace_afs_edit_dir(vnode, why, afs_edit_dir_create_inval, 0, 0, 0, 0, name->name);
345*4882a593Smuzhiyun 	clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags);
346*4882a593Smuzhiyun 	if (page != page0) {
347*4882a593Smuzhiyun 		kunmap(page);
348*4882a593Smuzhiyun 		put_page(page);
349*4882a593Smuzhiyun 	}
350*4882a593Smuzhiyun 	goto out_unmap;
351*4882a593Smuzhiyun 
352*4882a593Smuzhiyun error:
353*4882a593Smuzhiyun 	trace_afs_edit_dir(vnode, why, afs_edit_dir_create_error, 0, 0, 0, 0, name->name);
354*4882a593Smuzhiyun 	clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags);
355*4882a593Smuzhiyun 	goto out_unmap;
356*4882a593Smuzhiyun }
357*4882a593Smuzhiyun 
358*4882a593Smuzhiyun /*
359*4882a593Smuzhiyun  * Edit a directory's file data to remove a new directory entry.  Doing this
360*4882a593Smuzhiyun  * after unlink, rmdir or rename if the data version number is incremented by
361*4882a593Smuzhiyun  * exactly one avoids the need to re-download the entire directory contents.
362*4882a593Smuzhiyun  *
363*4882a593Smuzhiyun  * The caller must hold the inode locked.
364*4882a593Smuzhiyun  */
afs_edit_dir_remove(struct afs_vnode * vnode,struct qstr * name,enum afs_edit_dir_reason why)365*4882a593Smuzhiyun void afs_edit_dir_remove(struct afs_vnode *vnode,
366*4882a593Smuzhiyun 			 struct qstr *name, enum afs_edit_dir_reason why)
367*4882a593Smuzhiyun {
368*4882a593Smuzhiyun 	struct afs_xdr_dir_page *meta_page, *dir_page;
369*4882a593Smuzhiyun 	union afs_xdr_dir_block *meta, *block;
370*4882a593Smuzhiyun 	union afs_xdr_dirent *de;
371*4882a593Smuzhiyun 	struct page *page0, *page;
372*4882a593Smuzhiyun 	unsigned int need_slots, nr_blocks, b;
373*4882a593Smuzhiyun 	pgoff_t index;
374*4882a593Smuzhiyun 	loff_t i_size;
375*4882a593Smuzhiyun 	int slot;
376*4882a593Smuzhiyun 
377*4882a593Smuzhiyun 	_enter(",,{%d,%s},", name->len, name->name);
378*4882a593Smuzhiyun 
379*4882a593Smuzhiyun 	i_size = i_size_read(&vnode->vfs_inode);
380*4882a593Smuzhiyun 	if (i_size < AFS_DIR_BLOCK_SIZE ||
381*4882a593Smuzhiyun 	    i_size > AFS_DIR_BLOCK_SIZE * AFS_DIR_MAX_BLOCKS ||
382*4882a593Smuzhiyun 	    (i_size & (AFS_DIR_BLOCK_SIZE - 1))) {
383*4882a593Smuzhiyun 		clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags);
384*4882a593Smuzhiyun 		return;
385*4882a593Smuzhiyun 	}
386*4882a593Smuzhiyun 	nr_blocks = i_size / AFS_DIR_BLOCK_SIZE;
387*4882a593Smuzhiyun 
388*4882a593Smuzhiyun 	page0 = find_lock_page(vnode->vfs_inode.i_mapping, 0);
389*4882a593Smuzhiyun 	if (!page0) {
390*4882a593Smuzhiyun 		clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags);
391*4882a593Smuzhiyun 		_leave(" [fgp]");
392*4882a593Smuzhiyun 		return;
393*4882a593Smuzhiyun 	}
394*4882a593Smuzhiyun 
395*4882a593Smuzhiyun 	/* Work out how many slots we're going to discard. */
396*4882a593Smuzhiyun 	need_slots = round_up(12 + name->len + 1 + 4, AFS_DIR_DIRENT_SIZE);
397*4882a593Smuzhiyun 	need_slots /= AFS_DIR_DIRENT_SIZE;
398*4882a593Smuzhiyun 
399*4882a593Smuzhiyun 	meta_page = kmap(page0);
400*4882a593Smuzhiyun 	meta = &meta_page->blocks[0];
401*4882a593Smuzhiyun 
402*4882a593Smuzhiyun 	/* Find a page that has sufficient slots available.  Each VM page
403*4882a593Smuzhiyun 	 * contains two or more directory blocks.
404*4882a593Smuzhiyun 	 */
405*4882a593Smuzhiyun 	for (b = 0; b < nr_blocks; b++) {
406*4882a593Smuzhiyun 		index = b / AFS_DIR_BLOCKS_PER_PAGE;
407*4882a593Smuzhiyun 		if (index != 0) {
408*4882a593Smuzhiyun 			page = find_lock_page(vnode->vfs_inode.i_mapping, index);
409*4882a593Smuzhiyun 			if (!page)
410*4882a593Smuzhiyun 				goto error;
411*4882a593Smuzhiyun 			dir_page = kmap(page);
412*4882a593Smuzhiyun 		} else {
413*4882a593Smuzhiyun 			page = page0;
414*4882a593Smuzhiyun 			dir_page = meta_page;
415*4882a593Smuzhiyun 		}
416*4882a593Smuzhiyun 
417*4882a593Smuzhiyun 		/* Abandon the edit if we got a callback break. */
418*4882a593Smuzhiyun 		if (!test_bit(AFS_VNODE_DIR_VALID, &vnode->flags))
419*4882a593Smuzhiyun 			goto invalidated;
420*4882a593Smuzhiyun 
421*4882a593Smuzhiyun 		block = &dir_page->blocks[b % AFS_DIR_BLOCKS_PER_PAGE];
422*4882a593Smuzhiyun 
423*4882a593Smuzhiyun 		if (b > AFS_DIR_BLOCKS_WITH_CTR ||
424*4882a593Smuzhiyun 		    meta->meta.alloc_ctrs[b] <= AFS_DIR_SLOTS_PER_BLOCK - 1 - need_slots) {
425*4882a593Smuzhiyun 			slot = afs_dir_scan_block(block, name, b);
426*4882a593Smuzhiyun 			if (slot >= 0)
427*4882a593Smuzhiyun 				goto found_dirent;
428*4882a593Smuzhiyun 		}
429*4882a593Smuzhiyun 
430*4882a593Smuzhiyun 		if (page != page0) {
431*4882a593Smuzhiyun 			unlock_page(page);
432*4882a593Smuzhiyun 			kunmap(page);
433*4882a593Smuzhiyun 			put_page(page);
434*4882a593Smuzhiyun 		}
435*4882a593Smuzhiyun 	}
436*4882a593Smuzhiyun 
437*4882a593Smuzhiyun 	/* Didn't find the dirent to clobber.  Download the directory again. */
438*4882a593Smuzhiyun 	trace_afs_edit_dir(vnode, why, afs_edit_dir_delete_noent,
439*4882a593Smuzhiyun 			   0, 0, 0, 0, name->name);
440*4882a593Smuzhiyun 	clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags);
441*4882a593Smuzhiyun 	goto out_unmap;
442*4882a593Smuzhiyun 
443*4882a593Smuzhiyun found_dirent:
444*4882a593Smuzhiyun 	de = &block->dirents[slot];
445*4882a593Smuzhiyun 
446*4882a593Smuzhiyun 	trace_afs_edit_dir(vnode, why, afs_edit_dir_delete, b, slot,
447*4882a593Smuzhiyun 			   ntohl(de->u.vnode), ntohl(de->u.unique),
448*4882a593Smuzhiyun 			   name->name);
449*4882a593Smuzhiyun 
450*4882a593Smuzhiyun 	memset(de, 0, sizeof(*de) * need_slots);
451*4882a593Smuzhiyun 
452*4882a593Smuzhiyun 	/* Adjust the bitmap. */
453*4882a593Smuzhiyun 	afs_clear_contig_bits(block, slot, need_slots);
454*4882a593Smuzhiyun 	if (page != page0) {
455*4882a593Smuzhiyun 		unlock_page(page);
456*4882a593Smuzhiyun 		kunmap(page);
457*4882a593Smuzhiyun 		put_page(page);
458*4882a593Smuzhiyun 	}
459*4882a593Smuzhiyun 
460*4882a593Smuzhiyun 	/* Adjust the allocation counter. */
461*4882a593Smuzhiyun 	if (b < AFS_DIR_BLOCKS_WITH_CTR)
462*4882a593Smuzhiyun 		meta->meta.alloc_ctrs[b] += need_slots;
463*4882a593Smuzhiyun 
464*4882a593Smuzhiyun 	inode_set_iversion_raw(&vnode->vfs_inode, vnode->status.data_version);
465*4882a593Smuzhiyun 	afs_stat_v(vnode, n_dir_rm);
466*4882a593Smuzhiyun 	_debug("Remove %s from %u[%u]", name->name, b, slot);
467*4882a593Smuzhiyun 
468*4882a593Smuzhiyun out_unmap:
469*4882a593Smuzhiyun 	unlock_page(page0);
470*4882a593Smuzhiyun 	kunmap(page0);
471*4882a593Smuzhiyun 	put_page(page0);
472*4882a593Smuzhiyun 	_leave("");
473*4882a593Smuzhiyun 	return;
474*4882a593Smuzhiyun 
475*4882a593Smuzhiyun invalidated:
476*4882a593Smuzhiyun 	trace_afs_edit_dir(vnode, why, afs_edit_dir_delete_inval,
477*4882a593Smuzhiyun 			   0, 0, 0, 0, name->name);
478*4882a593Smuzhiyun 	clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags);
479*4882a593Smuzhiyun 	if (page != page0) {
480*4882a593Smuzhiyun 		unlock_page(page);
481*4882a593Smuzhiyun 		kunmap(page);
482*4882a593Smuzhiyun 		put_page(page);
483*4882a593Smuzhiyun 	}
484*4882a593Smuzhiyun 	goto out_unmap;
485*4882a593Smuzhiyun 
486*4882a593Smuzhiyun error:
487*4882a593Smuzhiyun 	trace_afs_edit_dir(vnode, why, afs_edit_dir_delete_error,
488*4882a593Smuzhiyun 			   0, 0, 0, 0, name->name);
489*4882a593Smuzhiyun 	clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags);
490*4882a593Smuzhiyun 	goto out_unmap;
491*4882a593Smuzhiyun }
492