blob: 35e5bebe4fab4bd5fe796615675db0131e358d67 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * namei.c
3 *
4 * PURPOSE
5 * Inode name handling routines for the OSTA-UDF(tm) filesystem.
6 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07007 * COPYRIGHT
8 * This file is distributed under the terms of the GNU General Public
9 * License (GPL). Copies of the GPL can be obtained from:
10 * ftp://prep.ai.mit.edu/pub/gnu/GPL
11 * Each contributing author retains all rights to their own work.
12 *
13 * (C) 1998-2004 Ben Fennema
14 * (C) 1999-2000 Stelias Computing Inc
15 *
16 * HISTORY
17 *
18 * 12/12/98 blf Created. Split out the lookup code from dir.c
19 * 04/19/99 blf link, mknod, symlink support
20 */
21
22#include "udfdecl.h"
23
24#include "udf_i.h"
25#include "udf_sb.h"
26#include <linux/string.h>
27#include <linux/errno.h>
28#include <linux/mm.h>
29#include <linux/slab.h>
30#include <linux/quotaops.h>
31#include <linux/smp_lock.h>
32#include <linux/buffer_head.h>
Alexey Dobriyane8edc6e2007-05-21 01:22:52 +040033#include <linux/sched.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070034
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -070035static inline int udf_match(int len1, const char *name1, int len2,
36 const char *name2)
Linus Torvalds1da177e2005-04-16 15:20:36 -070037{
38 if (len1 != len2)
39 return 0;
Cyrill Gorcunov28de7942007-07-21 04:37:18 -070040
Linus Torvalds1da177e2005-04-16 15:20:36 -070041 return !memcmp(name1, name2, len1);
42}
43
44int udf_write_fi(struct inode *inode, struct fileIdentDesc *cfi,
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -070045 struct fileIdentDesc *sfi, struct udf_fileident_bh *fibh,
Marcin Slusarz4b111112008-02-08 04:20:36 -080046 uint8_t *impuse, uint8_t *fileident)
Linus Torvalds1da177e2005-04-16 15:20:36 -070047{
48 uint16_t crclen = fibh->eoffset - fibh->soffset - sizeof(tag);
49 uint16_t crc;
Linus Torvalds1da177e2005-04-16 15:20:36 -070050 int offset;
51 uint16_t liu = le16_to_cpu(cfi->lengthOfImpUse);
52 uint8_t lfi = cfi->lengthFileIdent;
53 int padlen = fibh->eoffset - fibh->soffset - liu - lfi -
Cyrill Gorcunov28de7942007-07-21 04:37:18 -070054 sizeof(struct fileIdentDesc);
Linus Torvalds1da177e2005-04-16 15:20:36 -070055 int adinicb = 0;
56
Marcin Slusarzc0b34432008-02-08 04:20:42 -080057 if (UDF_I(inode)->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB)
Linus Torvalds1da177e2005-04-16 15:20:36 -070058 adinicb = 1;
59
60 offset = fibh->soffset + sizeof(struct fileIdentDesc);
61
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -070062 if (impuse) {
Cyrill Gorcunov28de7942007-07-21 04:37:18 -070063 if (adinicb || (offset + liu < 0)) {
64 memcpy((uint8_t *)sfi->impUse, impuse, liu);
65 } else if (offset >= 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -070066 memcpy(fibh->ebh->b_data + offset, impuse, liu);
Cyrill Gorcunov28de7942007-07-21 04:37:18 -070067 } else {
68 memcpy((uint8_t *)sfi->impUse, impuse, -offset);
Marcin Slusarz4b111112008-02-08 04:20:36 -080069 memcpy(fibh->ebh->b_data, impuse - offset,
70 liu + offset);
Linus Torvalds1da177e2005-04-16 15:20:36 -070071 }
72 }
73
74 offset += liu;
75
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -070076 if (fileident) {
Cyrill Gorcunov28de7942007-07-21 04:37:18 -070077 if (adinicb || (offset + lfi < 0)) {
78 memcpy((uint8_t *)sfi->fileIdent + liu, fileident, lfi);
79 } else if (offset >= 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -070080 memcpy(fibh->ebh->b_data + offset, fileident, lfi);
Cyrill Gorcunov28de7942007-07-21 04:37:18 -070081 } else {
Marcin Slusarz4b111112008-02-08 04:20:36 -080082 memcpy((uint8_t *)sfi->fileIdent + liu, fileident,
83 -offset);
84 memcpy(fibh->ebh->b_data, fileident - offset,
85 lfi + offset);
Linus Torvalds1da177e2005-04-16 15:20:36 -070086 }
87 }
88
89 offset += lfi;
90
Cyrill Gorcunov28de7942007-07-21 04:37:18 -070091 if (adinicb || (offset + padlen < 0)) {
92 memset((uint8_t *)sfi->padding + liu + lfi, 0x00, padlen);
93 } else if (offset >= 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -070094 memset(fibh->ebh->b_data + offset, 0x00, padlen);
Cyrill Gorcunov28de7942007-07-21 04:37:18 -070095 } else {
96 memset((uint8_t *)sfi->padding + liu + lfi, 0x00, -offset);
Linus Torvalds1da177e2005-04-16 15:20:36 -070097 memset(fibh->ebh->b_data, 0x00, padlen + offset);
98 }
99
Cyrill Gorcunov28de7942007-07-21 04:37:18 -0700100 crc = udf_crc((uint8_t *)cfi + sizeof(tag),
101 sizeof(struct fileIdentDesc) - sizeof(tag), 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700102
Cyrill Gorcunov28de7942007-07-21 04:37:18 -0700103 if (fibh->sbh == fibh->ebh) {
104 crc = udf_crc((uint8_t *)sfi->impUse,
Marcin Slusarz4b111112008-02-08 04:20:36 -0800105 crclen + sizeof(tag) -
106 sizeof(struct fileIdentDesc), crc);
Cyrill Gorcunov28de7942007-07-21 04:37:18 -0700107 } else if (sizeof(struct fileIdentDesc) >= -fibh->soffset) {
Marcin Slusarz4b111112008-02-08 04:20:36 -0800108 crc = udf_crc(fibh->ebh->b_data +
109 sizeof(struct fileIdentDesc) +
110 fibh->soffset,
111 crclen + sizeof(tag) -
112 sizeof(struct fileIdentDesc),
113 crc);
Cyrill Gorcunov28de7942007-07-21 04:37:18 -0700114 } else {
115 crc = udf_crc((uint8_t *)sfi->impUse,
Marcin Slusarz4b111112008-02-08 04:20:36 -0800116 -fibh->soffset - sizeof(struct fileIdentDesc),
117 crc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700118 crc = udf_crc(fibh->ebh->b_data, fibh->eoffset, crc);
119 }
120
121 cfi->descTag.descCRC = cpu_to_le16(crc);
122 cfi->descTag.descCRCLength = cpu_to_le16(crclen);
Marcin Slusarz3f2587b2008-02-08 04:20:39 -0800123 cfi->descTag.tagChecksum = udf_tag_checksum(&cfi->descTag);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700124
Cyrill Gorcunov28de7942007-07-21 04:37:18 -0700125 if (adinicb || (sizeof(struct fileIdentDesc) <= -fibh->soffset)) {
Marcin Slusarz4b111112008-02-08 04:20:36 -0800126 memcpy((uint8_t *)sfi, (uint8_t *)cfi,
127 sizeof(struct fileIdentDesc));
Cyrill Gorcunov28de7942007-07-21 04:37:18 -0700128 } else {
129 memcpy((uint8_t *)sfi, (uint8_t *)cfi, -fibh->soffset);
130 memcpy(fibh->ebh->b_data, (uint8_t *)cfi - fibh->soffset,
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700131 sizeof(struct fileIdentDesc) + fibh->soffset);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700132 }
133
Cyrill Gorcunov28de7942007-07-21 04:37:18 -0700134 if (adinicb) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700135 mark_inode_dirty(inode);
Cyrill Gorcunov28de7942007-07-21 04:37:18 -0700136 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700137 if (fibh->sbh != fibh->ebh)
138 mark_buffer_dirty_inode(fibh->ebh, inode);
139 mark_buffer_dirty_inode(fibh->sbh, inode);
140 }
141 return 0;
142}
143
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700144static struct fileIdentDesc *udf_find_entry(struct inode *dir,
145 struct dentry *dentry,
146 struct udf_fileident_bh *fibh,
147 struct fileIdentDesc *cfi)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700148{
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700149 struct fileIdentDesc *fi = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700150 loff_t f_pos;
151 int block, flen;
152 char fname[UDF_NAME_LEN];
153 char *nameptr;
154 uint8_t lfi;
155 uint16_t liu;
KAMBAROV, ZAURec471dc2005-06-28 20:45:10 -0700156 loff_t size;
Jan Karaff116fc2007-05-08 00:35:14 -0700157 kernel_lb_addr eloc;
158 uint32_t elen;
Jan Kara60448b12007-05-08 00:35:13 -0700159 sector_t offset;
Cyrill Gorcunov28de7942007-07-21 04:37:18 -0700160 struct extent_position epos = {};
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800161 struct udf_inode_info *dinfo = UDF_I(dir);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700162
KAMBAROV, ZAURec471dc2005-06-28 20:45:10 -0700163 size = (udf_ext0_offset(dir) + dir->i_size) >> 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700164 f_pos = (udf_ext0_offset(dir) >> 2);
165
Marcin Slusarz4b111112008-02-08 04:20:36 -0800166 fibh->soffset = fibh->eoffset =
167 (f_pos & ((dir->i_sb->s_blocksize - 1) >> 2)) << 2;
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800168 if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700169 fibh->sbh = fibh->ebh = NULL;
Marcin Slusarz4b111112008-02-08 04:20:36 -0800170 else if (inode_bmap(dir, f_pos >> (dir->i_sb->s_blocksize_bits - 2),
171 &epos, &eloc, &elen, &offset) ==
172 (EXT_RECORDED_ALLOCATED >> 30)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173 block = udf_get_lb_pblock(dir->i_sb, eloc, offset);
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700174 if ((++offset << dir->i_sb->s_blocksize_bits) < elen) {
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800175 if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT)
Jan Karaff116fc2007-05-08 00:35:14 -0700176 epos.offset -= sizeof(short_ad);
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800177 else if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_LONG)
Jan Karaff116fc2007-05-08 00:35:14 -0700178 epos.offset -= sizeof(long_ad);
Marcin Slusarz4b111112008-02-08 04:20:36 -0800179 } else
Linus Torvalds1da177e2005-04-16 15:20:36 -0700180 offset = 0;
181
Marcin Slusarz4b111112008-02-08 04:20:36 -0800182 fibh->sbh = fibh->ebh = udf_tread(dir->i_sb, block);
183 if (!fibh->sbh) {
Jan Kara3bf25cb2007-05-08 00:35:16 -0700184 brelse(epos.bh);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700185 return NULL;
186 }
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700187 } else {
Jan Kara3bf25cb2007-05-08 00:35:16 -0700188 brelse(epos.bh);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700189 return NULL;
190 }
191
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700192 while ((f_pos < size)) {
193 fi = udf_fileident_read(dir, &f_pos, fibh, cfi, &epos, &eloc,
194 &elen, &offset);
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700195 if (!fi) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700196 if (fibh->sbh != fibh->ebh)
Jan Kara3bf25cb2007-05-08 00:35:16 -0700197 brelse(fibh->ebh);
198 brelse(fibh->sbh);
199 brelse(epos.bh);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700200 return NULL;
201 }
202
203 liu = le16_to_cpu(cfi->lengthOfImpUse);
204 lfi = cfi->lengthFileIdent;
205
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700206 if (fibh->sbh == fibh->ebh) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700207 nameptr = fi->fileIdent + liu;
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700208 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700209 int poffset; /* Unpaded ending offset */
210
Marcin Slusarz4b111112008-02-08 04:20:36 -0800211 poffset = fibh->soffset + sizeof(struct fileIdentDesc) +
212 liu + lfi;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700213
Marcin Slusarz4b111112008-02-08 04:20:36 -0800214 if (poffset >= lfi)
215 nameptr = (uint8_t *)(fibh->ebh->b_data +
216 poffset - lfi);
217 else {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700218 nameptr = fname;
Marcin Slusarz4b111112008-02-08 04:20:36 -0800219 memcpy(nameptr, fi->fileIdent + liu,
220 lfi - poffset);
221 memcpy(nameptr + lfi - poffset,
222 fibh->ebh->b_data, poffset);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700223 }
224 }
225
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700226 if ((cfi->fileCharacteristics & FID_FILE_CHAR_DELETED) != 0) {
227 if (!UDF_QUERY_FLAG(dir->i_sb, UDF_FLAG_UNDELETE))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700228 continue;
229 }
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700230
231 if ((cfi->fileCharacteristics & FID_FILE_CHAR_HIDDEN) != 0) {
232 if (!UDF_QUERY_FLAG(dir->i_sb, UDF_FLAG_UNHIDE))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700233 continue;
234 }
235
236 if (!lfi)
237 continue;
238
Marcin Slusarz4b111112008-02-08 04:20:36 -0800239 flen = udf_get_filename(dir->i_sb, nameptr, fname, lfi);
240 if (flen && udf_match(flen, fname, dentry->d_name.len,
241 dentry->d_name.name)) {
242 brelse(epos.bh);
243 return fi;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700244 }
245 }
Cyrill Gorcunov28de7942007-07-21 04:37:18 -0700246
Linus Torvalds1da177e2005-04-16 15:20:36 -0700247 if (fibh->sbh != fibh->ebh)
Jan Kara3bf25cb2007-05-08 00:35:16 -0700248 brelse(fibh->ebh);
249 brelse(fibh->sbh);
250 brelse(epos.bh);
Cyrill Gorcunov28de7942007-07-21 04:37:18 -0700251
Linus Torvalds1da177e2005-04-16 15:20:36 -0700252 return NULL;
253}
254
255/*
256 * udf_lookup
257 *
258 * PURPOSE
259 * Look-up the inode for a given name.
260 *
261 * DESCRIPTION
262 * Required - lookup_dentry() will return -ENOTDIR if this routine is not
263 * available for a directory. The filesystem is useless if this routine is
264 * not available for at least the filesystem's root directory.
265 *
266 * This routine is passed an incomplete dentry - it must be completed by
267 * calling d_add(dentry, inode). If the name does not exist, then the
268 * specified inode must be set to null. An error should only be returned
269 * when the lookup fails for a reason other than the name not existing.
270 * Note that the directory inode semaphore is held during the call.
271 *
272 * Refer to lookup_dentry() in fs/namei.c
273 * lookup_dentry() -> lookup() -> real_lookup() -> .
274 *
275 * PRE-CONDITIONS
276 * dir Pointer to inode of parent directory.
277 * dentry Pointer to dentry to complete.
278 * nd Pointer to lookup nameidata
279 *
280 * POST-CONDITIONS
281 * <return> Zero on success.
282 *
283 * HISTORY
284 * July 1, 1997 - Andrew E. Mileski
285 * Written, tested, and released.
286 */
287
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700288static struct dentry *udf_lookup(struct inode *dir, struct dentry *dentry,
289 struct nameidata *nd)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700290{
291 struct inode *inode = NULL;
Jayachandran Cdb9a3692006-02-03 03:04:50 -0800292 struct fileIdentDesc cfi;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700293 struct udf_fileident_bh fibh;
294
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700295 if (dentry->d_name.len > UDF_NAME_LEN - 2)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700296 return ERR_PTR(-ENAMETOOLONG);
297
298 lock_kernel();
299#ifdef UDF_RECOVERY
300 /* temporary shorthand for specifying files by inode number */
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700301 if (!strncmp(dentry->d_name.name, ".B=", 3)) {
Cyrill Gorcunov28de7942007-07-21 04:37:18 -0700302 kernel_lb_addr lb = {
303 .logicalBlockNum = 0,
Marcin Slusarz4b111112008-02-08 04:20:36 -0800304 .partitionReferenceNum =
305 simple_strtoul(dentry->d_name.name + 3,
306 NULL, 0),
Cyrill Gorcunov28de7942007-07-21 04:37:18 -0700307 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700308 inode = udf_iget(dir->i_sb, lb);
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700309 if (!inode) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700310 unlock_kernel();
311 return ERR_PTR(-EACCES);
312 }
Marcin Slusarz4b111112008-02-08 04:20:36 -0800313 } else
Cyrill Gorcunov28de7942007-07-21 04:37:18 -0700314#endif /* UDF_RECOVERY */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700315
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700316 if (udf_find_entry(dir, dentry, &fibh, &cfi)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700317 if (fibh.sbh != fibh.ebh)
Jan Kara3bf25cb2007-05-08 00:35:16 -0700318 brelse(fibh.ebh);
319 brelse(fibh.sbh);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700320
321 inode = udf_iget(dir->i_sb, lelb_to_cpu(cfi.icb.extLocation));
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700322 if (!inode) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700323 unlock_kernel();
324 return ERR_PTR(-EACCES);
325 }
326 }
327 unlock_kernel();
328 d_add(dentry, inode);
Cyrill Gorcunov28de7942007-07-21 04:37:18 -0700329
Linus Torvalds1da177e2005-04-16 15:20:36 -0700330 return NULL;
331}
332
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700333static struct fileIdentDesc *udf_add_entry(struct inode *dir,
334 struct dentry *dentry,
335 struct udf_fileident_bh *fibh,
336 struct fileIdentDesc *cfi, int *err)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700337{
Marcin Slusarz6c79e982008-02-08 04:20:30 -0800338 struct super_block *sb = dir->i_sb;
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700339 struct fileIdentDesc *fi = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700340 char name[UDF_NAME_LEN], fname[UDF_NAME_LEN];
341 int namelen;
342 loff_t f_pos;
343 int flen;
344 char *nameptr;
345 loff_t size = (udf_ext0_offset(dir) + dir->i_size) >> 2;
346 int nfidlen;
347 uint8_t lfi;
348 uint16_t liu;
349 int block;
Jan Karaff116fc2007-05-08 00:35:14 -0700350 kernel_lb_addr eloc;
351 uint32_t elen;
Jan Kara60448b12007-05-08 00:35:13 -0700352 sector_t offset;
Cyrill Gorcunov28de7942007-07-21 04:37:18 -0700353 struct extent_position epos = {};
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800354 struct udf_inode_info *dinfo;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700355
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700356 if (dentry) {
357 if (!dentry->d_name.len) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700358 *err = -EINVAL;
359 return NULL;
360 }
Marcin Slusarz4b111112008-02-08 04:20:36 -0800361 namelen = udf_put_filename(sb, dentry->d_name.name, name,
362 dentry->d_name.len);
363 if (!namelen) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700364 *err = -ENAMETOOLONG;
365 return NULL;
366 }
Cyrill Gorcunov28de7942007-07-21 04:37:18 -0700367 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700368 namelen = 0;
Cyrill Gorcunov28de7942007-07-21 04:37:18 -0700369 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700370
371 nfidlen = (sizeof(struct fileIdentDesc) + namelen + 3) & ~3;
372
373 f_pos = (udf_ext0_offset(dir) >> 2);
374
Marcin Slusarz4b111112008-02-08 04:20:36 -0800375 fibh->soffset = fibh->eoffset =
376 (f_pos & ((dir->i_sb->s_blocksize - 1) >> 2)) << 2;
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800377 dinfo = UDF_I(dir);
378 if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700379 fibh->sbh = fibh->ebh = NULL;
Marcin Slusarz4b111112008-02-08 04:20:36 -0800380 else if (inode_bmap(dir, f_pos >> (dir->i_sb->s_blocksize_bits - 2),
381 &epos, &eloc, &elen, &offset) ==
382 (EXT_RECORDED_ALLOCATED >> 30)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700383 block = udf_get_lb_pblock(dir->i_sb, eloc, offset);
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700384 if ((++offset << dir->i_sb->s_blocksize_bits) < elen) {
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800385 if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT)
Jan Karaff116fc2007-05-08 00:35:14 -0700386 epos.offset -= sizeof(short_ad);
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800387 else if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_LONG)
Jan Karaff116fc2007-05-08 00:35:14 -0700388 epos.offset -= sizeof(long_ad);
Marcin Slusarz4b111112008-02-08 04:20:36 -0800389 } else
Linus Torvalds1da177e2005-04-16 15:20:36 -0700390 offset = 0;
391
Marcin Slusarz4b111112008-02-08 04:20:36 -0800392 fibh->sbh = fibh->ebh = udf_tread(dir->i_sb, block);
393 if (!fibh->sbh) {
Jan Kara3bf25cb2007-05-08 00:35:16 -0700394 brelse(epos.bh);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700395 *err = -EIO;
396 return NULL;
397 }
398
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800399 block = dinfo->i_location.logicalBlockNum;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700400
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700401 } else {
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800402 block = udf_get_lb_pblock(dir->i_sb, dinfo->i_location, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700403 fibh->sbh = fibh->ebh = NULL;
404 fibh->soffset = fibh->eoffset = sb->s_blocksize;
405 goto add;
406 }
407
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700408 while ((f_pos < size)) {
409 fi = udf_fileident_read(dir, &f_pos, fibh, cfi, &epos, &eloc,
410 &elen, &offset);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700411
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700412 if (!fi) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700413 if (fibh->sbh != fibh->ebh)
Jan Kara3bf25cb2007-05-08 00:35:16 -0700414 brelse(fibh->ebh);
415 brelse(fibh->sbh);
416 brelse(epos.bh);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700417 *err = -EIO;
418 return NULL;
419 }
420
421 liu = le16_to_cpu(cfi->lengthOfImpUse);
422 lfi = cfi->lengthFileIdent;
423
Marcin Slusarz4b111112008-02-08 04:20:36 -0800424 if (fibh->sbh == fibh->ebh)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700425 nameptr = fi->fileIdent + liu;
Marcin Slusarz4b111112008-02-08 04:20:36 -0800426 else {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700427 int poffset; /* Unpaded ending offset */
428
Marcin Slusarz4b111112008-02-08 04:20:36 -0800429 poffset = fibh->soffset + sizeof(struct fileIdentDesc) +
430 liu + lfi;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700431
Marcin Slusarz4b111112008-02-08 04:20:36 -0800432 if (poffset >= lfi)
433 nameptr = (char *)(fibh->ebh->b_data +
434 poffset - lfi);
435 else {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700436 nameptr = fname;
Marcin Slusarz4b111112008-02-08 04:20:36 -0800437 memcpy(nameptr, fi->fileIdent + liu,
438 lfi - poffset);
439 memcpy(nameptr + lfi - poffset,
440 fibh->ebh->b_data, poffset);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700441 }
442 }
443
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700444 if ((cfi->fileCharacteristics & FID_FILE_CHAR_DELETED) != 0) {
Marcin Slusarz4b111112008-02-08 04:20:36 -0800445 if (((sizeof(struct fileIdentDesc) +
446 liu + lfi + 3) & ~3) == nfidlen) {
Jan Kara3bf25cb2007-05-08 00:35:16 -0700447 brelse(epos.bh);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700448 cfi->descTag.tagSerialNum = cpu_to_le16(1);
449 cfi->fileVersionNum = cpu_to_le16(1);
450 cfi->fileCharacteristics = 0;
451 cfi->lengthFileIdent = namelen;
452 cfi->lengthOfImpUse = cpu_to_le16(0);
Marcin Slusarz4b111112008-02-08 04:20:36 -0800453 if (!udf_write_fi(dir, cfi, fi, fibh, NULL,
454 name))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700455 return fi;
Marcin Slusarz4b111112008-02-08 04:20:36 -0800456 else {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700457 *err = -EIO;
458 return NULL;
459 }
460 }
461 }
462
463 if (!lfi || !dentry)
464 continue;
465
Marcin Slusarz4b111112008-02-08 04:20:36 -0800466 flen = udf_get_filename(dir->i_sb, nameptr, fname, lfi);
467 if (flen && udf_match(flen, fname, dentry->d_name.len,
468 dentry->d_name.name)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700469 if (fibh->sbh != fibh->ebh)
Jan Kara3bf25cb2007-05-08 00:35:16 -0700470 brelse(fibh->ebh);
471 brelse(fibh->sbh);
472 brelse(epos.bh);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700473 *err = -EEXIST;
474 return NULL;
475 }
476 }
477
Cyrill Gorcunov28de7942007-07-21 04:37:18 -0700478add:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700479 f_pos += nfidlen;
480
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800481 if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB &&
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700482 sb->s_blocksize - fibh->eoffset < nfidlen) {
Jan Kara3bf25cb2007-05-08 00:35:16 -0700483 brelse(epos.bh);
Jan Karaff116fc2007-05-08 00:35:14 -0700484 epos.bh = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700485 fibh->soffset -= udf_ext0_offset(dir);
486 fibh->eoffset -= udf_ext0_offset(dir);
487 f_pos -= (udf_ext0_offset(dir) >> 2);
488 if (fibh->sbh != fibh->ebh)
Jan Kara3bf25cb2007-05-08 00:35:16 -0700489 brelse(fibh->ebh);
490 brelse(fibh->sbh);
Marcin Slusarz4b111112008-02-08 04:20:36 -0800491 fibh->sbh = fibh->ebh =
492 udf_expand_dir_adinicb(dir, &block, err);
493 if (!fibh->sbh)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700494 return NULL;
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800495 epos.block = dinfo->i_location;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700496 eloc.logicalBlockNum = block;
Marcin Slusarz4b111112008-02-08 04:20:36 -0800497 eloc.partitionReferenceNum =
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800498 dinfo->i_location.partitionReferenceNum;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700499 elen = dir->i_sb->s_blocksize;
Jan Karaff116fc2007-05-08 00:35:14 -0700500 epos.offset = udf_file_entry_alloc_offset(dir);
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800501 if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT)
Jan Karaff116fc2007-05-08 00:35:14 -0700502 epos.offset += sizeof(short_ad);
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800503 else if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_LONG)
Jan Karaff116fc2007-05-08 00:35:14 -0700504 epos.offset += sizeof(long_ad);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700505 }
506
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700507 if (sb->s_blocksize - fibh->eoffset >= nfidlen) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700508 fibh->soffset = fibh->eoffset;
509 fibh->eoffset += nfidlen;
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700510 if (fibh->sbh != fibh->ebh) {
Jan Kara3bf25cb2007-05-08 00:35:16 -0700511 brelse(fibh->sbh);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700512 fibh->sbh = fibh->ebh;
513 }
514
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800515 if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {
516 block = dinfo->i_location.logicalBlockNum;
Marcin Slusarz4b111112008-02-08 04:20:36 -0800517 fi = (struct fileIdentDesc *)
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800518 (dinfo->i_ext.i_data +
Marcin Slusarzc0b34432008-02-08 04:20:42 -0800519 fibh->soffset -
Marcin Slusarz4b111112008-02-08 04:20:36 -0800520 udf_ext0_offset(dir) +
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800521 dinfo->i_lenEAttr);
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700522 } else {
Marcin Slusarz4b111112008-02-08 04:20:36 -0800523 block = eloc.logicalBlockNum +
524 ((elen - 1) >>
525 dir->i_sb->s_blocksize_bits);
526 fi = (struct fileIdentDesc *)
527 (fibh->sbh->b_data + fibh->soffset);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700528 }
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700529 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700530 fibh->soffset = fibh->eoffset - sb->s_blocksize;
531 fibh->eoffset += nfidlen - sb->s_blocksize;
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700532 if (fibh->sbh != fibh->ebh) {
Jan Kara3bf25cb2007-05-08 00:35:16 -0700533 brelse(fibh->sbh);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700534 fibh->sbh = fibh->ebh;
535 }
536
537 block = eloc.logicalBlockNum + ((elen - 1) >>
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700538 dir->i_sb->s_blocksize_bits);
Marcin Slusarz4b111112008-02-08 04:20:36 -0800539 fibh->ebh = udf_bread(dir,
540 f_pos >> (dir->i_sb->s_blocksize_bits - 2),
541 1, err);
Cyrill Gorcunov28de7942007-07-21 04:37:18 -0700542 if (!fibh->ebh) {
Jan Kara3bf25cb2007-05-08 00:35:16 -0700543 brelse(epos.bh);
544 brelse(fibh->sbh);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700545 return NULL;
546 }
547
Cyrill Gorcunov28de7942007-07-21 04:37:18 -0700548 if (!fibh->soffset) {
Jan Karaff116fc2007-05-08 00:35:14 -0700549 if (udf_next_aext(dir, &epos, &eloc, &elen, 1) ==
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700550 (EXT_RECORDED_ALLOCATED >> 30)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700551 block = eloc.logicalBlockNum + ((elen - 1) >>
Cyrill Gorcunov28de7942007-07-21 04:37:18 -0700552 dir->i_sb->s_blocksize_bits);
Marcin Slusarz4b111112008-02-08 04:20:36 -0800553 } else
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700554 block++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700555
Jan Kara3bf25cb2007-05-08 00:35:16 -0700556 brelse(fibh->sbh);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700557 fibh->sbh = fibh->ebh;
558 fi = (struct fileIdentDesc *)(fibh->sbh->b_data);
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700559 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700560 fi = (struct fileIdentDesc *)
Marcin Slusarz4b111112008-02-08 04:20:36 -0800561 (fibh->sbh->b_data + sb->s_blocksize +
562 fibh->soffset);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700563 }
564 }
565
566 memset(cfi, 0, sizeof(struct fileIdentDesc));
Marcin Slusarz6c79e982008-02-08 04:20:30 -0800567 if (UDF_SB(sb)->s_udfrev >= 0x0200)
Marcin Slusarz4b111112008-02-08 04:20:36 -0800568 udf_new_tag((char *)cfi, TAG_IDENT_FID, 3, 1, block,
569 sizeof(tag));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700570 else
Marcin Slusarz4b111112008-02-08 04:20:36 -0800571 udf_new_tag((char *)cfi, TAG_IDENT_FID, 2, 1, block,
572 sizeof(tag));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700573 cfi->fileVersionNum = cpu_to_le16(1);
574 cfi->lengthFileIdent = namelen;
575 cfi->lengthOfImpUse = cpu_to_le16(0);
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700576 if (!udf_write_fi(dir, cfi, fi, fibh, NULL, name)) {
Jan Kara3bf25cb2007-05-08 00:35:16 -0700577 brelse(epos.bh);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700578 dir->i_size += nfidlen;
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800579 if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB)
580 dinfo->i_lenAlloc += nfidlen;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700581 mark_inode_dirty(dir);
582 return fi;
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700583 } else {
Jan Kara3bf25cb2007-05-08 00:35:16 -0700584 brelse(epos.bh);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700585 if (fibh->sbh != fibh->ebh)
Jan Kara3bf25cb2007-05-08 00:35:16 -0700586 brelse(fibh->ebh);
587 brelse(fibh->sbh);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700588 *err = -EIO;
589 return NULL;
590 }
591}
592
593static int udf_delete_entry(struct inode *inode, struct fileIdentDesc *fi,
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700594 struct udf_fileident_bh *fibh,
595 struct fileIdentDesc *cfi)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700596{
597 cfi->fileCharacteristics |= FID_FILE_CHAR_DELETED;
Cyrill Gorcunov28de7942007-07-21 04:37:18 -0700598
Linus Torvalds1da177e2005-04-16 15:20:36 -0700599 if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT))
600 memset(&(cfi->icb), 0x00, sizeof(long_ad));
Cyrill Gorcunov28de7942007-07-21 04:37:18 -0700601
Linus Torvalds1da177e2005-04-16 15:20:36 -0700602 return udf_write_fi(inode, cfi, fi, fibh, NULL, NULL);
603}
604
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700605static int udf_create(struct inode *dir, struct dentry *dentry, int mode,
606 struct nameidata *nd)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700607{
608 struct udf_fileident_bh fibh;
609 struct inode *inode;
610 struct fileIdentDesc cfi, *fi;
611 int err;
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800612 struct udf_inode_info *iinfo;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700613
614 lock_kernel();
615 inode = udf_new_inode(dir, mode, &err);
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700616 if (!inode) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700617 unlock_kernel();
618 return err;
619 }
620
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800621 iinfo = UDF_I(inode);
622 if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700623 inode->i_data.a_ops = &udf_adinicb_aops;
624 else
625 inode->i_data.a_ops = &udf_aops;
626 inode->i_op = &udf_file_inode_operations;
627 inode->i_fop = &udf_file_operations;
628 inode->i_mode = mode;
629 mark_inode_dirty(inode);
630
Marcin Slusarz4b111112008-02-08 04:20:36 -0800631 fi = udf_add_entry(dir, dentry, &fibh, &cfi, &err);
632 if (!fi) {
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700633 inode->i_nlink--;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700634 mark_inode_dirty(inode);
635 iput(inode);
636 unlock_kernel();
637 return err;
638 }
639 cfi.icb.extLength = cpu_to_le32(inode->i_sb->s_blocksize);
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800640 cfi.icb.extLocation = cpu_to_lelb(iinfo->i_location);
Cyrill Gorcunov28de7942007-07-21 04:37:18 -0700641 *(__le32 *)((struct allocDescImpUse *)cfi.icb.impUse)->impUse =
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800642 cpu_to_le32(iinfo->i_unique & 0x00000000FFFFFFFFUL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700643 udf_write_fi(dir, &cfi, fi, &fibh, NULL, NULL);
Marcin Slusarzc0b34432008-02-08 04:20:42 -0800644 if (UDF_I(dir)->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700645 mark_inode_dirty(dir);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700646 if (fibh.sbh != fibh.ebh)
Jan Kara3bf25cb2007-05-08 00:35:16 -0700647 brelse(fibh.ebh);
648 brelse(fibh.sbh);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700649 unlock_kernel();
650 d_instantiate(dentry, inode);
Cyrill Gorcunov28de7942007-07-21 04:37:18 -0700651
Linus Torvalds1da177e2005-04-16 15:20:36 -0700652 return 0;
653}
654
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700655static int udf_mknod(struct inode *dir, struct dentry *dentry, int mode,
656 dev_t rdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700657{
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700658 struct inode *inode;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700659 struct udf_fileident_bh fibh;
660 struct fileIdentDesc cfi, *fi;
661 int err;
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800662 struct udf_inode_info *iinfo;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700663
664 if (!old_valid_dev(rdev))
665 return -EINVAL;
666
667 lock_kernel();
668 err = -EIO;
669 inode = udf_new_inode(dir, mode, &err);
670 if (!inode)
671 goto out;
672
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800673 iinfo = UDF_I(inode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700674 inode->i_uid = current->fsuid;
675 init_special_inode(inode, mode, rdev);
Marcin Slusarz4b111112008-02-08 04:20:36 -0800676 fi = udf_add_entry(dir, dentry, &fibh, &cfi, &err);
677 if (!fi) {
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700678 inode->i_nlink--;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700679 mark_inode_dirty(inode);
680 iput(inode);
681 unlock_kernel();
682 return err;
683 }
684 cfi.icb.extLength = cpu_to_le32(inode->i_sb->s_blocksize);
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800685 cfi.icb.extLocation = cpu_to_lelb(iinfo->i_location);
Cyrill Gorcunov28de7942007-07-21 04:37:18 -0700686 *(__le32 *)((struct allocDescImpUse *)cfi.icb.impUse)->impUse =
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800687 cpu_to_le32(iinfo->i_unique & 0x00000000FFFFFFFFUL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700688 udf_write_fi(dir, &cfi, fi, &fibh, NULL, NULL);
Marcin Slusarzc0b34432008-02-08 04:20:42 -0800689 if (UDF_I(dir)->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700690 mark_inode_dirty(dir);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700691 mark_inode_dirty(inode);
692
693 if (fibh.sbh != fibh.ebh)
Jan Kara3bf25cb2007-05-08 00:35:16 -0700694 brelse(fibh.ebh);
695 brelse(fibh.sbh);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700696 d_instantiate(dentry, inode);
697 err = 0;
Cyrill Gorcunov28de7942007-07-21 04:37:18 -0700698
699out:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700700 unlock_kernel();
701 return err;
702}
703
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700704static int udf_mkdir(struct inode *dir, struct dentry *dentry, int mode)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700705{
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700706 struct inode *inode;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700707 struct udf_fileident_bh fibh;
708 struct fileIdentDesc cfi, *fi;
709 int err;
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800710 struct udf_inode_info *dinfo = UDF_I(dir);
711 struct udf_inode_info *iinfo;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700712
713 lock_kernel();
714 err = -EMLINK;
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700715 if (dir->i_nlink >= (256 << sizeof(dir->i_nlink)) - 1)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700716 goto out;
717
718 err = -EIO;
719 inode = udf_new_inode(dir, S_IFDIR, &err);
720 if (!inode)
721 goto out;
722
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800723 iinfo = UDF_I(inode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700724 inode->i_op = &udf_dir_inode_operations;
725 inode->i_fop = &udf_dir_operations;
Marcin Slusarz4b111112008-02-08 04:20:36 -0800726 fi = udf_add_entry(inode, NULL, &fibh, &cfi, &err);
727 if (!fi) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700728 inode->i_nlink--;
729 mark_inode_dirty(inode);
730 iput(inode);
731 goto out;
732 }
733 inode->i_nlink = 2;
734 cfi.icb.extLength = cpu_to_le32(inode->i_sb->s_blocksize);
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800735 cfi.icb.extLocation = cpu_to_lelb(dinfo->i_location);
Cyrill Gorcunov28de7942007-07-21 04:37:18 -0700736 *(__le32 *)((struct allocDescImpUse *)cfi.icb.impUse)->impUse =
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800737 cpu_to_le32(dinfo->i_unique & 0x00000000FFFFFFFFUL);
Marcin Slusarz4b111112008-02-08 04:20:36 -0800738 cfi.fileCharacteristics =
739 FID_FILE_CHAR_DIRECTORY | FID_FILE_CHAR_PARENT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700740 udf_write_fi(inode, &cfi, fi, &fibh, NULL, NULL);
Jan Kara3bf25cb2007-05-08 00:35:16 -0700741 brelse(fibh.sbh);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700742 inode->i_mode = S_IFDIR | mode;
743 if (dir->i_mode & S_ISGID)
744 inode->i_mode |= S_ISGID;
745 mark_inode_dirty(inode);
746
Marcin Slusarz4b111112008-02-08 04:20:36 -0800747 fi = udf_add_entry(dir, dentry, &fibh, &cfi, &err);
748 if (!fi) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700749 inode->i_nlink = 0;
750 mark_inode_dirty(inode);
751 iput(inode);
752 goto out;
753 }
754 cfi.icb.extLength = cpu_to_le32(inode->i_sb->s_blocksize);
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800755 cfi.icb.extLocation = cpu_to_lelb(iinfo->i_location);
Cyrill Gorcunov28de7942007-07-21 04:37:18 -0700756 *(__le32 *)((struct allocDescImpUse *)cfi.icb.impUse)->impUse =
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800757 cpu_to_le32(iinfo->i_unique & 0x00000000FFFFFFFFUL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700758 cfi.fileCharacteristics |= FID_FILE_CHAR_DIRECTORY;
759 udf_write_fi(dir, &cfi, fi, &fibh, NULL, NULL);
Dave Hansend8c76e62006-09-30 23:29:04 -0700760 inc_nlink(dir);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700761 mark_inode_dirty(dir);
762 d_instantiate(dentry, inode);
763 if (fibh.sbh != fibh.ebh)
Jan Kara3bf25cb2007-05-08 00:35:16 -0700764 brelse(fibh.ebh);
765 brelse(fibh.sbh);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700766 err = 0;
Cyrill Gorcunov28de7942007-07-21 04:37:18 -0700767
768out:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700769 unlock_kernel();
770 return err;
771}
772
773static int empty_dir(struct inode *dir)
774{
775 struct fileIdentDesc *fi, cfi;
776 struct udf_fileident_bh fibh;
777 loff_t f_pos;
778 loff_t size = (udf_ext0_offset(dir) + dir->i_size) >> 2;
779 int block;
Jan Karaff116fc2007-05-08 00:35:14 -0700780 kernel_lb_addr eloc;
781 uint32_t elen;
Jan Kara60448b12007-05-08 00:35:13 -0700782 sector_t offset;
Cyrill Gorcunov28de7942007-07-21 04:37:18 -0700783 struct extent_position epos = {};
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800784 struct udf_inode_info *dinfo = UDF_I(dir);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700785
786 f_pos = (udf_ext0_offset(dir) >> 2);
787
Marcin Slusarz4b111112008-02-08 04:20:36 -0800788 fibh.soffset = fibh.eoffset =
789 (f_pos & ((dir->i_sb->s_blocksize - 1) >> 2)) << 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700790
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800791 if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700792 fibh.sbh = fibh.ebh = NULL;
Marcin Slusarz4b111112008-02-08 04:20:36 -0800793 else if (inode_bmap(dir, f_pos >> (dir->i_sb->s_blocksize_bits - 2),
794 &epos, &eloc, &elen, &offset) ==
795 (EXT_RECORDED_ALLOCATED >> 30)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700796 block = udf_get_lb_pblock(dir->i_sb, eloc, offset);
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700797 if ((++offset << dir->i_sb->s_blocksize_bits) < elen) {
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800798 if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT)
Jan Karaff116fc2007-05-08 00:35:14 -0700799 epos.offset -= sizeof(short_ad);
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800800 else if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_LONG)
Jan Karaff116fc2007-05-08 00:35:14 -0700801 epos.offset -= sizeof(long_ad);
Marcin Slusarz4b111112008-02-08 04:20:36 -0800802 } else
Linus Torvalds1da177e2005-04-16 15:20:36 -0700803 offset = 0;
804
Marcin Slusarz4b111112008-02-08 04:20:36 -0800805 fibh.sbh = fibh.ebh = udf_tread(dir->i_sb, block);
806 if (!fibh.sbh) {
Jan Kara3bf25cb2007-05-08 00:35:16 -0700807 brelse(epos.bh);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700808 return 0;
809 }
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700810 } else {
Jan Kara3bf25cb2007-05-08 00:35:16 -0700811 brelse(epos.bh);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700812 return 0;
813 }
814
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700815 while ((f_pos < size)) {
816 fi = udf_fileident_read(dir, &f_pos, &fibh, &cfi, &epos, &eloc,
817 &elen, &offset);
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700818 if (!fi) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700819 if (fibh.sbh != fibh.ebh)
Jan Kara3bf25cb2007-05-08 00:35:16 -0700820 brelse(fibh.ebh);
821 brelse(fibh.sbh);
822 brelse(epos.bh);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700823 return 0;
824 }
825
Cyrill Gorcunov28de7942007-07-21 04:37:18 -0700826 if (cfi.lengthFileIdent &&
827 (cfi.fileCharacteristics & FID_FILE_CHAR_DELETED) == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700828 if (fibh.sbh != fibh.ebh)
Jan Kara3bf25cb2007-05-08 00:35:16 -0700829 brelse(fibh.ebh);
830 brelse(fibh.sbh);
831 brelse(epos.bh);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700832 return 0;
833 }
834 }
Cyrill Gorcunov28de7942007-07-21 04:37:18 -0700835
Linus Torvalds1da177e2005-04-16 15:20:36 -0700836 if (fibh.sbh != fibh.ebh)
Jan Kara3bf25cb2007-05-08 00:35:16 -0700837 brelse(fibh.ebh);
838 brelse(fibh.sbh);
839 brelse(epos.bh);
Cyrill Gorcunov28de7942007-07-21 04:37:18 -0700840
Linus Torvalds1da177e2005-04-16 15:20:36 -0700841 return 1;
842}
843
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700844static int udf_rmdir(struct inode *dir, struct dentry *dentry)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700845{
846 int retval;
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700847 struct inode *inode = dentry->d_inode;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700848 struct udf_fileident_bh fibh;
849 struct fileIdentDesc *fi, cfi;
850 kernel_lb_addr tloc;
851
852 retval = -ENOENT;
853 lock_kernel();
854 fi = udf_find_entry(dir, dentry, &fibh, &cfi);
855 if (!fi)
856 goto out;
857
858 retval = -EIO;
859 tloc = lelb_to_cpu(cfi.icb.extLocation);
860 if (udf_get_lb_pblock(dir->i_sb, tloc, 0) != inode->i_ino)
861 goto end_rmdir;
862 retval = -ENOTEMPTY;
863 if (!empty_dir(inode))
864 goto end_rmdir;
865 retval = udf_delete_entry(dir, fi, &fibh, &cfi);
866 if (retval)
867 goto end_rmdir;
868 if (inode->i_nlink != 2)
869 udf_warning(inode->i_sb, "udf_rmdir",
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700870 "empty directory has nlink != 2 (%d)",
871 inode->i_nlink);
Dave Hansence71ec32006-09-30 23:29:06 -0700872 clear_nlink(inode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700873 inode->i_size = 0;
Stephen Mollettc007c062007-05-08 00:31:31 -0700874 inode_dec_link_count(dir);
Marcin Slusarz4b111112008-02-08 04:20:36 -0800875 inode->i_ctime = dir->i_ctime = dir->i_mtime =
876 current_fs_time(dir->i_sb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700877 mark_inode_dirty(dir);
878
Cyrill Gorcunov28de7942007-07-21 04:37:18 -0700879end_rmdir:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700880 if (fibh.sbh != fibh.ebh)
Jan Kara3bf25cb2007-05-08 00:35:16 -0700881 brelse(fibh.ebh);
882 brelse(fibh.sbh);
Cyrill Gorcunov28de7942007-07-21 04:37:18 -0700883
884out:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700885 unlock_kernel();
886 return retval;
887}
888
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700889static int udf_unlink(struct inode *dir, struct dentry *dentry)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700890{
891 int retval;
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700892 struct inode *inode = dentry->d_inode;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700893 struct udf_fileident_bh fibh;
894 struct fileIdentDesc *fi;
895 struct fileIdentDesc cfi;
896 kernel_lb_addr tloc;
897
898 retval = -ENOENT;
899 lock_kernel();
900 fi = udf_find_entry(dir, dentry, &fibh, &cfi);
901 if (!fi)
902 goto out;
903
904 retval = -EIO;
905 tloc = lelb_to_cpu(cfi.icb.extLocation);
906 if (udf_get_lb_pblock(dir->i_sb, tloc, 0) != inode->i_ino)
907 goto end_unlink;
908
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700909 if (!inode->i_nlink) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700910 udf_debug("Deleting nonexistent file (%lu), %d\n",
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700911 inode->i_ino, inode->i_nlink);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700912 inode->i_nlink = 1;
913 }
914 retval = udf_delete_entry(dir, fi, &fibh, &cfi);
915 if (retval)
916 goto end_unlink;
917 dir->i_ctime = dir->i_mtime = current_fs_time(dir->i_sb);
918 mark_inode_dirty(dir);
Dave Hansen9a53c3a2006-09-30 23:29:03 -0700919 inode_dec_link_count(inode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700920 inode->i_ctime = dir->i_ctime;
921 retval = 0;
922
Cyrill Gorcunov28de7942007-07-21 04:37:18 -0700923end_unlink:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700924 if (fibh.sbh != fibh.ebh)
Jan Kara3bf25cb2007-05-08 00:35:16 -0700925 brelse(fibh.ebh);
926 brelse(fibh.sbh);
Cyrill Gorcunov28de7942007-07-21 04:37:18 -0700927
928out:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700929 unlock_kernel();
930 return retval;
931}
932
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700933static int udf_symlink(struct inode *dir, struct dentry *dentry,
934 const char *symname)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700935{
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700936 struct inode *inode;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700937 struct pathComponent *pc;
938 char *compstart;
939 struct udf_fileident_bh fibh;
Cyrill Gorcunov28de7942007-07-21 04:37:18 -0700940 struct extent_position epos = {};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700941 int eoffset, elen = 0;
942 struct fileIdentDesc *fi;
943 struct fileIdentDesc cfi;
944 char *ea;
945 int err;
946 int block;
947 char name[UDF_NAME_LEN];
948 int namelen;
Marcin Slusarz6c79e982008-02-08 04:20:30 -0800949 struct buffer_head *bh;
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800950 struct udf_inode_info *iinfo;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700951
952 lock_kernel();
Marcin Slusarz4b111112008-02-08 04:20:36 -0800953 inode = udf_new_inode(dir, S_IFLNK, &err);
954 if (!inode)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700955 goto out;
956
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800957 iinfo = UDF_I(inode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700958 inode->i_mode = S_IFLNK | S_IRWXUGO;
959 inode->i_data.a_ops = &udf_symlink_aops;
960 inode->i_op = &page_symlink_inode_operations;
961
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800962 if (iinfo->i_alloc_type != ICBTAG_FLAG_AD_IN_ICB) {
Jan Karaff116fc2007-05-08 00:35:14 -0700963 kernel_lb_addr eloc;
964 uint32_t elen;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700965
966 block = udf_new_block(inode->i_sb, inode,
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800967 iinfo->i_location.partitionReferenceNum,
968 iinfo->i_location.logicalBlockNum, &err);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700969 if (!block)
970 goto out_no_entry;
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800971 epos.block = iinfo->i_location;
Jan Karaff116fc2007-05-08 00:35:14 -0700972 epos.offset = udf_file_entry_alloc_offset(inode);
973 epos.bh = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700974 eloc.logicalBlockNum = block;
Marcin Slusarz4b111112008-02-08 04:20:36 -0800975 eloc.partitionReferenceNum =
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800976 iinfo->i_location.partitionReferenceNum;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700977 elen = inode->i_sb->s_blocksize;
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800978 iinfo->i_lenExtents = elen;
Jan Karaff116fc2007-05-08 00:35:14 -0700979 udf_add_aext(inode, &epos, eloc, elen, 0);
Jan Kara3bf25cb2007-05-08 00:35:16 -0700980 brelse(epos.bh);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700981
982 block = udf_get_pblock(inode->i_sb, block,
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800983 iinfo->i_location.partitionReferenceNum,
Marcin Slusarz4b111112008-02-08 04:20:36 -0800984 0);
Jan Karaff116fc2007-05-08 00:35:14 -0700985 epos.bh = udf_tread(inode->i_sb, block);
986 lock_buffer(epos.bh);
987 memset(epos.bh->b_data, 0x00, inode->i_sb->s_blocksize);
988 set_buffer_uptodate(epos.bh);
989 unlock_buffer(epos.bh);
990 mark_buffer_dirty_inode(epos.bh, inode);
991 ea = epos.bh->b_data + udf_ext0_offset(inode);
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -0800992 } else
993 ea = iinfo->i_ext.i_data + iinfo->i_lenEAttr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700994
995 eoffset = inode->i_sb->s_blocksize - udf_ext0_offset(inode);
996 pc = (struct pathComponent *)ea;
997
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -0700998 if (*symname == '/') {
999 do {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001000 symname++;
1001 } while (*symname == '/');
1002
1003 pc->componentType = 1;
1004 pc->lengthComponentIdent = 0;
1005 pc->componentFileVersionNum = 0;
1006 pc += sizeof(struct pathComponent);
1007 elen += sizeof(struct pathComponent);
1008 }
1009
1010 err = -ENAMETOOLONG;
1011
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -07001012 while (*symname) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001013 if (elen + sizeof(struct pathComponent) > eoffset)
1014 goto out_no_entry;
1015
1016 pc = (struct pathComponent *)(ea + elen);
1017
1018 compstart = (char *)symname;
1019
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -07001020 do {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001021 symname++;
1022 } while (*symname && *symname != '/');
1023
1024 pc->componentType = 5;
1025 pc->lengthComponentIdent = 0;
1026 pc->componentFileVersionNum = 0;
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -07001027 if (compstart[0] == '.') {
1028 if ((symname - compstart) == 1)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001029 pc->componentType = 4;
Marcin Slusarz4b111112008-02-08 04:20:36 -08001030 else if ((symname - compstart) == 2 &&
1031 compstart[1] == '.')
Linus Torvalds1da177e2005-04-16 15:20:36 -07001032 pc->componentType = 3;
1033 }
1034
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -07001035 if (pc->componentType == 5) {
Cyrill Gorcunov28de7942007-07-21 04:37:18 -07001036 namelen = udf_put_filename(inode->i_sb, compstart, name,
1037 symname - compstart);
1038 if (!namelen)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001039 goto out_no_entry;
1040
Marcin Slusarz4b111112008-02-08 04:20:36 -08001041 if (elen + sizeof(struct pathComponent) + namelen >
1042 eoffset)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001043 goto out_no_entry;
1044 else
1045 pc->lengthComponentIdent = namelen;
1046
1047 memcpy(pc->componentIdent, name, namelen);
1048 }
1049
1050 elen += sizeof(struct pathComponent) + pc->lengthComponentIdent;
1051
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -07001052 if (*symname) {
1053 do {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001054 symname++;
1055 } while (*symname == '/');
1056 }
1057 }
1058
Jan Kara3bf25cb2007-05-08 00:35:16 -07001059 brelse(epos.bh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001060 inode->i_size = elen;
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -08001061 if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB)
1062 iinfo->i_lenAlloc = inode->i_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001063 mark_inode_dirty(inode);
1064
Marcin Slusarz4b111112008-02-08 04:20:36 -08001065 fi = udf_add_entry(dir, dentry, &fibh, &cfi, &err);
1066 if (!fi)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001067 goto out_no_entry;
1068 cfi.icb.extLength = cpu_to_le32(inode->i_sb->s_blocksize);
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -08001069 cfi.icb.extLocation = cpu_to_lelb(iinfo->i_location);
Marcin Slusarz6c79e982008-02-08 04:20:30 -08001070 bh = UDF_SB(inode->i_sb)->s_lvid_bh;
1071 if (bh) {
Marcin Slusarz4b111112008-02-08 04:20:36 -08001072 struct logicalVolIntegrityDesc *lvid =
1073 (struct logicalVolIntegrityDesc *)bh->b_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001074 struct logicalVolHeaderDesc *lvhd;
1075 uint64_t uniqueID;
Marcin Slusarz4b111112008-02-08 04:20:36 -08001076 lvhd = (struct logicalVolHeaderDesc *)
1077 lvid->logicalVolContentsUse;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001078 uniqueID = le64_to_cpu(lvhd->uniqueID);
Cyrill Gorcunov28de7942007-07-21 04:37:18 -07001079 *(__le32 *)((struct allocDescImpUse *)cfi.icb.impUse)->impUse =
1080 cpu_to_le32(uniqueID & 0x00000000FFFFFFFFUL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001081 if (!(++uniqueID & 0x00000000FFFFFFFFUL))
1082 uniqueID += 16;
1083 lvhd->uniqueID = cpu_to_le64(uniqueID);
Marcin Slusarz6c79e982008-02-08 04:20:30 -08001084 mark_buffer_dirty(bh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001085 }
1086 udf_write_fi(dir, &cfi, fi, &fibh, NULL, NULL);
Marcin Slusarzc0b34432008-02-08 04:20:42 -08001087 if (UDF_I(dir)->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001088 mark_inode_dirty(dir);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001089 if (fibh.sbh != fibh.ebh)
Jan Kara3bf25cb2007-05-08 00:35:16 -07001090 brelse(fibh.ebh);
1091 brelse(fibh.sbh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001092 d_instantiate(dentry, inode);
1093 err = 0;
1094
Cyrill Gorcunov28de7942007-07-21 04:37:18 -07001095out:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001096 unlock_kernel();
1097 return err;
1098
Cyrill Gorcunov28de7942007-07-21 04:37:18 -07001099out_no_entry:
Dave Hansen9a53c3a2006-09-30 23:29:03 -07001100 inode_dec_link_count(inode);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001101 iput(inode);
1102 goto out;
1103}
1104
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -07001105static int udf_link(struct dentry *old_dentry, struct inode *dir,
1106 struct dentry *dentry)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001107{
1108 struct inode *inode = old_dentry->d_inode;
1109 struct udf_fileident_bh fibh;
1110 struct fileIdentDesc cfi, *fi;
1111 int err;
Marcin Slusarz6c79e982008-02-08 04:20:30 -08001112 struct buffer_head *bh;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001113
1114 lock_kernel();
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -07001115 if (inode->i_nlink >= (256 << sizeof(inode->i_nlink)) - 1) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001116 unlock_kernel();
1117 return -EMLINK;
1118 }
1119
Marcin Slusarz4b111112008-02-08 04:20:36 -08001120 fi = udf_add_entry(dir, dentry, &fibh, &cfi, &err);
1121 if (!fi) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001122 unlock_kernel();
1123 return err;
1124 }
1125 cfi.icb.extLength = cpu_to_le32(inode->i_sb->s_blocksize);
Marcin Slusarzc0b34432008-02-08 04:20:42 -08001126 cfi.icb.extLocation = cpu_to_lelb(UDF_I(inode)->i_location);
Marcin Slusarz6c79e982008-02-08 04:20:30 -08001127 bh = UDF_SB(inode->i_sb)->s_lvid_bh;
1128 if (bh) {
Marcin Slusarz4b111112008-02-08 04:20:36 -08001129 struct logicalVolIntegrityDesc *lvid =
1130 (struct logicalVolIntegrityDesc *)bh->b_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001131 struct logicalVolHeaderDesc *lvhd;
1132 uint64_t uniqueID;
Marcin Slusarz4b111112008-02-08 04:20:36 -08001133 lvhd = (struct logicalVolHeaderDesc *)
1134 (lvid->logicalVolContentsUse);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001135 uniqueID = le64_to_cpu(lvhd->uniqueID);
Cyrill Gorcunov28de7942007-07-21 04:37:18 -07001136 *(__le32 *)((struct allocDescImpUse *)cfi.icb.impUse)->impUse =
1137 cpu_to_le32(uniqueID & 0x00000000FFFFFFFFUL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001138 if (!(++uniqueID & 0x00000000FFFFFFFFUL))
1139 uniqueID += 16;
1140 lvhd->uniqueID = cpu_to_le64(uniqueID);
Marcin Slusarz6c79e982008-02-08 04:20:30 -08001141 mark_buffer_dirty(bh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001142 }
1143 udf_write_fi(dir, &cfi, fi, &fibh, NULL, NULL);
Marcin Slusarzc0b34432008-02-08 04:20:42 -08001144 if (UDF_I(dir)->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001145 mark_inode_dirty(dir);
Cyrill Gorcunov28de7942007-07-21 04:37:18 -07001146
Linus Torvalds1da177e2005-04-16 15:20:36 -07001147 if (fibh.sbh != fibh.ebh)
Jan Kara3bf25cb2007-05-08 00:35:16 -07001148 brelse(fibh.ebh);
1149 brelse(fibh.sbh);
Dave Hansend8c76e62006-09-30 23:29:04 -07001150 inc_nlink(inode);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001151 inode->i_ctime = current_fs_time(inode->i_sb);
1152 mark_inode_dirty(inode);
1153 atomic_inc(&inode->i_count);
1154 d_instantiate(dentry, inode);
1155 unlock_kernel();
Cyrill Gorcunov28de7942007-07-21 04:37:18 -07001156
Linus Torvalds1da177e2005-04-16 15:20:36 -07001157 return 0;
1158}
1159
1160/* Anybody can rename anything with this: the permission checks are left to the
1161 * higher-level routines.
1162 */
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -07001163static int udf_rename(struct inode *old_dir, struct dentry *old_dentry,
1164 struct inode *new_dir, struct dentry *new_dentry)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001165{
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -07001166 struct inode *old_inode = old_dentry->d_inode;
1167 struct inode *new_inode = new_dentry->d_inode;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001168 struct udf_fileident_bh ofibh, nfibh;
Marcin Slusarz4b111112008-02-08 04:20:36 -08001169 struct fileIdentDesc *ofi = NULL, *nfi = NULL, *dir_fi = NULL;
1170 struct fileIdentDesc ocfi, ncfi;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001171 struct buffer_head *dir_bh = NULL;
1172 int retval = -ENOENT;
1173 kernel_lb_addr tloc;
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -08001174 struct udf_inode_info *old_iinfo = UDF_I(old_inode);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001175
1176 lock_kernel();
Marcin Slusarz4b111112008-02-08 04:20:36 -08001177 ofi = udf_find_entry(old_dir, old_dentry, &ofibh, &ocfi);
1178 if (ofi) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001179 if (ofibh.sbh != ofibh.ebh)
Jan Kara3bf25cb2007-05-08 00:35:16 -07001180 brelse(ofibh.ebh);
1181 brelse(ofibh.sbh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001182 }
1183 tloc = lelb_to_cpu(ocfi.icb.extLocation);
1184 if (!ofi || udf_get_lb_pblock(old_dir->i_sb, tloc, 0)
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -07001185 != old_inode->i_ino)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001186 goto end_rename;
1187
1188 nfi = udf_find_entry(new_dir, new_dentry, &nfibh, &ncfi);
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -07001189 if (nfi) {
1190 if (!new_inode) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001191 if (nfibh.sbh != nfibh.ebh)
Jan Kara3bf25cb2007-05-08 00:35:16 -07001192 brelse(nfibh.ebh);
1193 brelse(nfibh.sbh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001194 nfi = NULL;
1195 }
1196 }
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -07001197 if (S_ISDIR(old_inode->i_mode)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001198 uint32_t offset = udf_ext0_offset(old_inode);
1199
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -07001200 if (new_inode) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001201 retval = -ENOTEMPTY;
1202 if (!empty_dir(new_inode))
1203 goto end_rename;
1204 }
1205 retval = -EIO;
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -08001206 if (old_iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {
Marcin Slusarz4b111112008-02-08 04:20:36 -08001207 dir_fi = udf_get_fileident(
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -08001208 old_iinfo->i_ext.i_data -
1209 (old_iinfo->i_efe ?
Marcin Slusarz4b111112008-02-08 04:20:36 -08001210 sizeof(struct extendedFileEntry) :
1211 sizeof(struct fileEntry)),
1212 old_inode->i_sb->s_blocksize, &offset);
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -07001213 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001214 dir_bh = udf_bread(old_inode, 0, 0, &retval);
1215 if (!dir_bh)
1216 goto end_rename;
Marcin Slusarz4b111112008-02-08 04:20:36 -08001217 dir_fi = udf_get_fileident(dir_bh->b_data,
1218 old_inode->i_sb->s_blocksize, &offset);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001219 }
1220 if (!dir_fi)
1221 goto end_rename;
1222 tloc = lelb_to_cpu(dir_fi->icb.extLocation);
Marcin Slusarz4b111112008-02-08 04:20:36 -08001223 if (udf_get_lb_pblock(old_inode->i_sb, tloc, 0) !=
1224 old_dir->i_ino)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001225 goto end_rename;
1226
1227 retval = -EMLINK;
Marcin Slusarz4b111112008-02-08 04:20:36 -08001228 if (!new_inode &&
1229 new_dir->i_nlink >=
1230 (256 << sizeof(new_dir->i_nlink)) - 1)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001231 goto end_rename;
1232 }
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -07001233 if (!nfi) {
Marcin Slusarz4b111112008-02-08 04:20:36 -08001234 nfi = udf_add_entry(new_dir, new_dentry, &nfibh, &ncfi,
1235 &retval);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001236 if (!nfi)
1237 goto end_rename;
1238 }
1239
1240 /*
1241 * Like most other Unix systems, set the ctime for inodes on a
1242 * rename.
1243 */
1244 old_inode->i_ctime = current_fs_time(old_inode->i_sb);
1245 mark_inode_dirty(old_inode);
1246
1247 /*
1248 * ok, that's it
1249 */
1250 ncfi.fileVersionNum = ocfi.fileVersionNum;
1251 ncfi.fileCharacteristics = ocfi.fileCharacteristics;
1252 memcpy(&(ncfi.icb), &(ocfi.icb), sizeof(long_ad));
1253 udf_write_fi(new_dir, &ncfi, nfi, &nfibh, NULL, NULL);
1254
1255 /* The old fid may have moved - find it again */
1256 ofi = udf_find_entry(old_dir, old_dentry, &ofibh, &ocfi);
1257 udf_delete_entry(old_dir, ofi, &ofibh, &ocfi);
1258
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -07001259 if (new_inode) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001260 new_inode->i_ctime = current_fs_time(new_inode->i_sb);
Dave Hansen9a53c3a2006-09-30 23:29:03 -07001261 inode_dec_link_count(new_inode);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001262 }
1263 old_dir->i_ctime = old_dir->i_mtime = current_fs_time(old_dir->i_sb);
1264 mark_inode_dirty(old_dir);
1265
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -07001266 if (dir_fi) {
Marcin Slusarzc0b34432008-02-08 04:20:42 -08001267 dir_fi->icb.extLocation = cpu_to_lelb(UDF_I(new_dir)->i_location);
Marcin Slusarz4b111112008-02-08 04:20:36 -08001268 udf_update_tag((char *)dir_fi,
1269 (sizeof(struct fileIdentDesc) +
1270 le16_to_cpu(dir_fi->lengthOfImpUse) + 3) & ~3);
Marcin Slusarz48d6d8f2008-02-08 04:20:44 -08001271 if (old_iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001272 mark_inode_dirty(old_inode);
Marcin Slusarz4b111112008-02-08 04:20:36 -08001273 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001274 mark_buffer_dirty_inode(dir_bh, old_inode);
Marcin Slusarz4b111112008-02-08 04:20:36 -08001275
Dave Hansen9a53c3a2006-09-30 23:29:03 -07001276 inode_dec_link_count(old_dir);
Marcin Slusarz4b111112008-02-08 04:20:36 -08001277 if (new_inode)
Dave Hansen9a53c3a2006-09-30 23:29:03 -07001278 inode_dec_link_count(new_inode);
Marcin Slusarz4b111112008-02-08 04:20:36 -08001279 else {
Dave Hansend8c76e62006-09-30 23:29:04 -07001280 inc_nlink(new_dir);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001281 mark_inode_dirty(new_dir);
1282 }
1283 }
1284
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -07001285 if (ofi) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001286 if (ofibh.sbh != ofibh.ebh)
Jan Kara3bf25cb2007-05-08 00:35:16 -07001287 brelse(ofibh.ebh);
1288 brelse(ofibh.sbh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001289 }
1290
1291 retval = 0;
1292
Cyrill Gorcunov28de7942007-07-21 04:37:18 -07001293end_rename:
Jan Kara3bf25cb2007-05-08 00:35:16 -07001294 brelse(dir_bh);
Cyrill Gorcunovcb00ea32007-07-19 01:47:43 -07001295 if (nfi) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001296 if (nfibh.sbh != nfibh.ebh)
Jan Kara3bf25cb2007-05-08 00:35:16 -07001297 brelse(nfibh.ebh);
1298 brelse(nfibh.sbh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001299 }
1300 unlock_kernel();
Cyrill Gorcunov28de7942007-07-21 04:37:18 -07001301
Linus Torvalds1da177e2005-04-16 15:20:36 -07001302 return retval;
1303}
1304
Arjan van de Venc5ef1c42007-02-12 00:55:40 -08001305const struct inode_operations udf_dir_inode_operations = {
Cyrill Gorcunov28de7942007-07-21 04:37:18 -07001306 .lookup = udf_lookup,
1307 .create = udf_create,
1308 .link = udf_link,
1309 .unlink = udf_unlink,
1310 .symlink = udf_symlink,
1311 .mkdir = udf_mkdir,
1312 .rmdir = udf_rmdir,
1313 .mknod = udf_mknod,
1314 .rename = udf_rename,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001315};