|
Line 0
Link Here
|
|
|
1 |
/* |
| 2 |
* |
| 3 |
* Amiga Smart File System, Linux implementation |
| 4 |
* version: 1.0beta3 |
| 5 |
* |
| 6 |
* This file contains some parts of the original amiga version of |
| 7 |
* SmartFilesystem source code. |
| 8 |
* |
| 9 |
* SmartFilesystem is copyrighted (C) 2003 by: John Hendrikx, |
| 10 |
* Ralph Schmidt, Emmanuel Lesueur, David Gerber, and Marcin Kurek |
| 11 |
* |
| 12 |
* Adapted and modified by Marek 'March' Szyprowski <marek@amiga.pl> |
| 13 |
* |
| 14 |
*/ |
| 15 |
|
| 16 |
#include <linux/types.h> |
| 17 |
#include <linux/errno.h> |
| 18 |
#include <linux/slab.h> |
| 19 |
#include <linux/asfs_fs.h> |
| 20 |
#include <linux/fs.h> |
| 21 |
#include <linux/buffer_head.h> |
| 22 |
#include <linux/vfs.h> |
| 23 |
|
| 24 |
#include <asm/byteorder.h> |
| 25 |
|
| 26 |
struct fsObject *asfs_nextobject(struct fsObject *obj) |
| 27 |
{ |
| 28 |
int i; |
| 29 |
u8 *p = obj->name; |
| 30 |
|
| 31 |
for (i = 2; i > 0; p++) |
| 32 |
if (*p == '\0') |
| 33 |
i--; |
| 34 |
if ((p - (u8 *) obj) & 0x01) |
| 35 |
p++; |
| 36 |
|
| 37 |
return ((struct fsObject *) p); |
| 38 |
} |
| 39 |
|
| 40 |
struct fsObject *find_obj_by_name(struct super_block *sb, struct fsObjectContainer *objcont, u8 * name) |
| 41 |
{ |
| 42 |
struct fsObject *obj; |
| 43 |
|
| 44 |
obj = &(objcont->object[0]); |
| 45 |
while (be32_to_cpu(obj->objectnode) > 0 && ((char *) obj - (char *) objcont) + sizeof(struct fsObject) + 2 < sb->s_blocksize) { |
| 46 |
if (asfs_namecmp(obj->name, name, ASFS_SB(sb)->flags & ASFS_ROOTBITS_CASESENSITIVE) == 0) { |
| 47 |
asfs_debug("Object found! Node %u, Name %s, Type %x, inCont %u\n", be32_to_cpu(obj->objectnode), obj->name, obj->bits, be32_to_cpu(objcont->bheader.ownblock)); |
| 48 |
return obj; |
| 49 |
} |
| 50 |
obj = asfs_nextobject(obj); |
| 51 |
} |
| 52 |
return NULL; |
| 53 |
} |
| 54 |
|
| 55 |
#ifdef CONFIG_ASFS_RW |
| 56 |
|
| 57 |
struct fsObject *find_obj_by_node(struct super_block *sb, struct fsObjectContainer *objcont, u32 objnode) |
| 58 |
{ |
| 59 |
struct fsObject *obj; |
| 60 |
|
| 61 |
obj = &(objcont->object[0]); |
| 62 |
while (be32_to_cpu(obj->objectnode) > 0 && ((char *) obj - (char *) objcont) + sizeof(struct fsObject) + 2 < sb->s_blocksize) { |
| 63 |
if (be32_to_cpu(obj->objectnode) == objnode) { |
| 64 |
return obj; |
| 65 |
} |
| 66 |
obj = asfs_nextobject(obj); |
| 67 |
} |
| 68 |
return NULL; |
| 69 |
} |
| 70 |
|
| 71 |
int readobject(struct super_block *sb, u32 objectnode, struct buffer_head **bh, struct fsObject **returned_object) |
| 72 |
{ |
| 73 |
struct fsObjectNode *on; |
| 74 |
int errorcode; |
| 75 |
u32 contblock; |
| 76 |
|
| 77 |
asfs_debug("Seaching object - node %d\n", objectnode); |
| 78 |
|
| 79 |
if ((errorcode = getnode(sb, objectnode, bh, &on)) != 0) |
| 80 |
return errorcode; |
| 81 |
contblock = be32_to_cpu(on->node.data); |
| 82 |
asfs_brelse(*bh); |
| 83 |
|
| 84 |
if (contblock > 0 && (*bh = asfs_breadcheck(sb, contblock, ASFS_OBJECTCONTAINER_ID))) { |
| 85 |
*returned_object = find_obj_by_node(sb, (void *) (*bh)->b_data, objectnode); |
| 86 |
if (*returned_object == NULL) { |
| 87 |
brelse(*bh); |
| 88 |
*bh = NULL; |
| 89 |
return -ENOENT; |
| 90 |
} |
| 91 |
return 0; |
| 92 |
} else |
| 93 |
return -EIO; |
| 94 |
} |
| 95 |
|
| 96 |
static int removeobjectcontainer(struct super_block *sb, struct buffer_head *bh) |
| 97 |
{ |
| 98 |
struct fsObjectContainer *oc = (void *) bh->b_data; |
| 99 |
int errorcode; |
| 100 |
struct buffer_head *block; |
| 101 |
|
| 102 |
asfs_debug("removeobjectcontainer: block %u\n", be32_to_cpu(oc->bheader.ownblock)); |
| 103 |
|
| 104 |
if (oc->next != 0 && oc->next != oc->bheader.ownblock) { |
| 105 |
struct fsObjectContainer *next_oc; |
| 106 |
|
| 107 |
if ((block = asfs_breadcheck(sb, be32_to_cpu(oc->next), ASFS_OBJECTCONTAINER_ID)) == NULL) |
| 108 |
return -EIO; |
| 109 |
|
| 110 |
next_oc = (void *) block->b_data; |
| 111 |
next_oc->previous = oc->previous; |
| 112 |
|
| 113 |
asfs_bstore(sb, block); |
| 114 |
asfs_brelse(block); |
| 115 |
} |
| 116 |
|
| 117 |
if (oc->previous != 0 && oc->previous != oc->bheader.ownblock) { |
| 118 |
struct fsObjectContainer *previous_oc; |
| 119 |
|
| 120 |
if ((block = asfs_breadcheck(sb, be32_to_cpu(oc->previous), ASFS_OBJECTCONTAINER_ID)) == NULL) |
| 121 |
return -EIO; |
| 122 |
|
| 123 |
previous_oc = (void *) block->b_data; |
| 124 |
previous_oc->next = oc->next; |
| 125 |
|
| 126 |
asfs_bstore(sb, block); |
| 127 |
asfs_brelse(block); |
| 128 |
} else { |
| 129 |
struct fsObject *parent_o; |
| 130 |
|
| 131 |
if ((errorcode = readobject(sb, be32_to_cpu(oc->parent), &block, &parent_o)) != 0) |
| 132 |
return (errorcode); |
| 133 |
|
| 134 |
parent_o->object.dir.firstdirblock = oc->next; |
| 135 |
|
| 136 |
asfs_bstore(sb, block); |
| 137 |
asfs_brelse(block); |
| 138 |
} |
| 139 |
|
| 140 |
if ((errorcode = freeadminspace(sb, be32_to_cpu(oc->bheader.ownblock))) != 0) |
| 141 |
return (errorcode); |
| 142 |
|
| 143 |
return (0); |
| 144 |
} |
| 145 |
|
| 146 |
int setrecycledinfodiff(struct super_block *sb, s32 deletedfiles, s32 deletedblocks) |
| 147 |
{ |
| 148 |
struct buffer_head *bh; |
| 149 |
|
| 150 |
if ((bh = asfs_breadcheck(sb, ASFS_SB(sb)->rootobjectcontainer, ASFS_OBJECTCONTAINER_ID))) { |
| 151 |
struct fsRootInfo *ri = (struct fsRootInfo *) ((u8 *) bh->b_data + sb->s_blocksize - sizeof(struct fsRootInfo)); |
| 152 |
|
| 153 |
ri->deletedfiles = cpu_to_be32(be32_to_cpu(ri->deletedfiles) + deletedfiles); |
| 154 |
ri->deletedblocks = cpu_to_be32(be32_to_cpu(ri->deletedblocks) + deletedblocks); |
| 155 |
|
| 156 |
asfs_bstore(sb, bh); |
| 157 |
asfs_brelse(bh); |
| 158 |
} else |
| 159 |
return -EIO; |
| 160 |
return 0; |
| 161 |
} |
| 162 |
|
| 163 |
/* This function removes the fsObject structure passed in from the passed |
| 164 |
buffer_head. If the ObjectContainer becomes completely empty it will be |
| 165 |
delinked from the ObjectContainer chain and marked free for reuse. |
| 166 |
This function doesn't delink the object from the hashchain! */ |
| 167 |
|
| 168 |
static int simpleremoveobject(struct super_block *sb, struct buffer_head *bh, struct fsObject *o) |
| 169 |
{ |
| 170 |
struct fsObjectContainer *oc = (void *) bh->b_data; |
| 171 |
int errorcode = 0; |
| 172 |
|
| 173 |
asfs_debug("simpleremoveobject:\n"); |
| 174 |
|
| 175 |
if (be32_to_cpu(oc->parent) == ASFS_RECYCLEDNODE) { |
| 176 |
/* This object is removed from the Recycled directory. */ |
| 177 |
if ((errorcode = setrecycledinfodiff(sb, -1, -((be32_to_cpu(o->object.file.size) + sb->s_blocksize - 1) >> sb->s_blocksize_bits))) != 0) |
| 178 |
return errorcode; |
| 179 |
} |
| 180 |
|
| 181 |
if ((asfs_nextobject(oc->object))->name[0] == '\0') |
| 182 |
errorcode = removeobjectcontainer(sb, bh); |
| 183 |
else { |
| 184 |
struct fsObject *nexto; |
| 185 |
int objlen; |
| 186 |
|
| 187 |
nexto = asfs_nextobject(o); |
| 188 |
objlen = (u8 *) nexto - (u8 *) o; |
| 189 |
|
| 190 |
memmove(o, nexto, sb->s_blocksize - ((u8 *) nexto - (u8 *) oc)); |
| 191 |
memset((u8 *) oc + sb->s_blocksize - objlen, 0, objlen); |
| 192 |
|
| 193 |
asfs_bstore(sb, bh); |
| 194 |
} |
| 195 |
return errorcode; |
| 196 |
} |
| 197 |
|
| 198 |
/* This function delinks the passed in ObjectNode from its hash-chain. Handy when deleting |
| 199 |
the object, or when renaming/moving it. */ |
| 200 |
|
| 201 |
static int dehashobjectquick(struct super_block *sb, u32 objectnode, u8 *name, u32 parentobjectnode) |
| 202 |
{ |
| 203 |
struct fsObject *o; |
| 204 |
int errorcode; |
| 205 |
struct buffer_head *block; |
| 206 |
|
| 207 |
asfs_debug("dehashobject: Delinking object %d (=ObjectNode) from hashchain. Parentnode = %d\n", objectnode, parentobjectnode); |
| 208 |
|
| 209 |
if ((errorcode = readobject(sb, parentobjectnode, &block, &o)) == 0 && o->object.dir.hashtable != 0) { |
| 210 |
u32 hashtable = be32_to_cpu(o->object.dir.hashtable); |
| 211 |
asfs_brelse(block); |
| 212 |
|
| 213 |
if ((block = asfs_breadcheck(sb, hashtable, ASFS_HASHTABLE_ID))) { |
| 214 |
struct buffer_head *node_bh; |
| 215 |
struct fsObjectNode *onptr, on; |
| 216 |
struct fsHashTable *ht = (void *) block->b_data; |
| 217 |
u32 nexthash; |
| 218 |
|
| 219 |
if ((errorcode = getnode(sb, objectnode, &node_bh, &onptr)) == 0) { |
| 220 |
u16 hashchain; |
| 221 |
|
| 222 |
asfs_debug("dehashobject: Read HashTable block of parent object of object to be delinked\n"); |
| 223 |
|
| 224 |
hashchain = HASHCHAIN(asfs_hash(name, ASFS_SB(sb)->flags & ASFS_ROOTBITS_CASESENSITIVE)); |
| 225 |
nexthash = be32_to_cpu(ht->hashentry[hashchain]); |
| 226 |
|
| 227 |
if (nexthash == objectnode) { |
| 228 |
/* The hashtable directly points to the fsObject to be delinked. We simply |
| 229 |
modify the Hashtable to point to the new nexthash entry. */ |
| 230 |
|
| 231 |
asfs_debug("dehashobject: The hashtable points directly to the to be delinked object\n"); |
| 232 |
|
| 233 |
ht->hashentry[hashchain] = onptr->next; |
| 234 |
asfs_bstore(sb, block); |
| 235 |
} else { |
| 236 |
struct fsObjectNode *onsearch = 0; |
| 237 |
|
| 238 |
on = *onptr; |
| 239 |
|
| 240 |
asfs_debug("dehashobject: Walking through hashchain\n"); |
| 241 |
|
| 242 |
while (nexthash != 0 && nexthash != objectnode) { |
| 243 |
asfs_brelse(node_bh); |
| 244 |
if ((errorcode = getnode(sb, nexthash, &node_bh, &onsearch)) != 0) |
| 245 |
break; |
| 246 |
nexthash = be32_to_cpu(onsearch->next); |
| 247 |
} |
| 248 |
|
| 249 |
if (errorcode == 0) { |
| 250 |
if (nexthash != 0) { |
| 251 |
/* Previous fsObjectNode found in hash chain. Modify the fsObjectNode to 'skip' the |
| 252 |
ObjectNode which is being delinked from the hash chain. */ |
| 253 |
|
| 254 |
onsearch->next = on.next; |
| 255 |
asfs_bstore(sb, node_bh); |
| 256 |
} else { |
| 257 |
printk("ASFS: Hashchain of object %d is corrupt or incorrectly linked.", objectnode); |
| 258 |
|
| 259 |
/*** This is strange. We have been looking for the fsObjectNode which is located before the |
| 260 |
passed in fsObjectNode in the hash-chain. However, we never found the |
| 261 |
fsObjectNode reffered to in the hash-chain! Has to be somekind |
| 262 |
of internal error... */ |
| 263 |
|
| 264 |
errorcode = -ENOENT; |
| 265 |
} |
| 266 |
} |
| 267 |
} |
| 268 |
asfs_brelse(node_bh); |
| 269 |
} |
| 270 |
asfs_brelse(block); |
| 271 |
} |
| 272 |
} |
| 273 |
return errorcode; |
| 274 |
} |
| 275 |
|
| 276 |
|
| 277 |
/* This function removes an object from any directory. It takes care |
| 278 |
of delinking the object from the hashchain and also frees the |
| 279 |
objectnode number. */ |
| 280 |
|
| 281 |
int removeobject(struct super_block *sb, struct buffer_head *bh, struct fsObject *o) |
| 282 |
{ |
| 283 |
struct fsObjectContainer *oc = (void *) bh->b_data; |
| 284 |
int errorcode; |
| 285 |
|
| 286 |
asfs_debug("removeobject\n"); |
| 287 |
|
| 288 |
if ((errorcode = dehashobjectquick(sb, be32_to_cpu(o->objectnode), o->name, be32_to_cpu(oc->parent))) == 0) { |
| 289 |
u32 objectnode = be32_to_cpu(o->objectnode); |
| 290 |
|
| 291 |
if ((errorcode = simpleremoveobject(sb, bh, o)) == 0) |
| 292 |
errorcode = deletenode(sb, objectnode); |
| 293 |
} |
| 294 |
|
| 295 |
return (errorcode); |
| 296 |
} |
| 297 |
|
| 298 |
/* This function deletes the specified object. */ |
| 299 |
int deleteobject(struct super_block *sb, struct buffer_head *bh, struct fsObject *o) |
| 300 |
{ |
| 301 |
int errorcode = 0; |
| 302 |
|
| 303 |
asfs_debug("deleteobject: Entry -- deleting object %d (%s)\n", be32_to_cpu(o->objectnode), o->name); |
| 304 |
|
| 305 |
if ((o->bits & OTYPE_DIR) == 0 || o->object.dir.firstdirblock == 0) { |
| 306 |
u8 bits = o->bits; |
| 307 |
u32 hashblckno = be32_to_cpu(o->object.dir.hashtable); |
| 308 |
u32 extentbnode = be32_to_cpu(o->object.file.data); |
| 309 |
|
| 310 |
if ((errorcode = removeobject(sb, bh, o)) == 0) { |
| 311 |
if ((bits & OTYPE_LINK) != 0) { |
| 312 |
asfs_debug("deleteobject: Object is soft link!\n"); |
| 313 |
errorcode = freeadminspace(sb, extentbnode); |
| 314 |
} else if ((bits & OTYPE_DIR) != 0) { |
| 315 |
asfs_debug("deleteobject: Object is a directory!\n"); |
| 316 |
errorcode = freeadminspace(sb, hashblckno); |
| 317 |
} else { |
| 318 |
asfs_debug("deleteobject: Object is a file\n"); |
| 319 |
if (extentbnode != 0) |
| 320 |
errorcode = deleteextents(sb, extentbnode); |
| 321 |
} |
| 322 |
} |
| 323 |
} |
| 324 |
|
| 325 |
return (errorcode); |
| 326 |
} |
| 327 |
|
| 328 |
/* This function takes a HashBlock pointer, an ObjectNode and an ObjectName. |
| 329 |
If there is a hashblock, then this function will correctly link the object |
| 330 |
into the hashchain. If there isn't a hashblock (=0) then this function |
| 331 |
does nothing. */ |
| 332 |
|
| 333 |
static int hashobject(struct super_block *sb, u32 hashblock, struct fsObjectNode *on, u32 nodeno, u8 *objectname) |
| 334 |
{ |
| 335 |
struct buffer_head *hash_bh; |
| 336 |
|
| 337 |
asfs_debug("hashobject, using hashblock %d\n", hashblock); |
| 338 |
if (hashblock == 0) |
| 339 |
return 0; |
| 340 |
|
| 341 |
if ((hash_bh = asfs_breadcheck(sb, hashblock, ASFS_HASHTABLE_ID))) { |
| 342 |
struct fsHashTable *ht = (void *) hash_bh->b_data; |
| 343 |
u32 nexthash; |
| 344 |
u16 hashvalue, hashchain; |
| 345 |
|
| 346 |
hashvalue = asfs_hash(objectname, ASFS_SB(sb)->flags & ASFS_ROOTBITS_CASESENSITIVE); |
| 347 |
hashchain = HASHCHAIN(hashvalue); |
| 348 |
nexthash = be32_to_cpu(ht->hashentry[hashchain]); |
| 349 |
|
| 350 |
ht->hashentry[hashchain] = cpu_to_be32(nodeno); |
| 351 |
|
| 352 |
asfs_bstore(sb, hash_bh); |
| 353 |
asfs_brelse(hash_bh); |
| 354 |
|
| 355 |
on->next = cpu_to_be32(nexthash); |
| 356 |
on->hash16 = cpu_to_be16(hashvalue); |
| 357 |
} else |
| 358 |
return -EIO; |
| 359 |
|
| 360 |
return 0; |
| 361 |
} |
| 362 |
|
| 363 |
/* This function returns a pointer to the first unused byte in |
| 364 |
an ObjectContainer. */ |
| 365 |
|
| 366 |
static u8 *emptyspaceinobjectcontainer(struct super_block *sb, struct fsObjectContainer *oc) |
| 367 |
{ |
| 368 |
struct fsObject *o = oc->object; |
| 369 |
u8 *endadr; |
| 370 |
|
| 371 |
endadr = (u8 *) oc + sb->s_blocksize - sizeof(struct fsObject) - 2; |
| 372 |
|
| 373 |
while ((u8 *) o < endadr && o->name[0] != 0) |
| 374 |
o = asfs_nextobject(o); |
| 375 |
|
| 376 |
return (u8 *) o; |
| 377 |
} |
| 378 |
|
| 379 |
/* This function will look in the directory indicated by io_o |
| 380 |
for an ObjectContainer block which contains bytesneeded free |
| 381 |
bytes. If none is found then this function simply creates a |
| 382 |
new ObjectContainer and adds that to the indicated directory. */ |
| 383 |
|
| 384 |
static int findobjectspace(struct super_block *sb, struct buffer_head **io_bh, struct fsObject **io_o, u32 bytesneeded) |
| 385 |
{ |
| 386 |
struct buffer_head *bhparent = *io_bh; |
| 387 |
struct fsObject *oparent = *io_o; |
| 388 |
struct buffer_head *bh; |
| 389 |
u32 nextblock = be32_to_cpu(oparent->object.dir.firstdirblock); |
| 390 |
int errorcode = 0; |
| 391 |
|
| 392 |
asfs_debug("findobjectspace: Looking for %u bytes in directory with ObjectNode number %d (in block %d)\n", bytesneeded, be32_to_cpu((*io_o)->objectnode), |
| 393 |
be32_to_cpu(((struct fsBlockHeader *) (*io_bh)->b_data)->ownblock)); |
| 394 |
|
| 395 |
while (nextblock != 0 && (bh = asfs_breadcheck(sb, nextblock, ASFS_OBJECTCONTAINER_ID))) { |
| 396 |
struct fsObjectContainer *oc = (void *) bh->b_data; |
| 397 |
u8 *emptyspace; |
| 398 |
|
| 399 |
/* We need to find out how much free space this ObjectContainer has */ |
| 400 |
|
| 401 |
emptyspace = emptyspaceinobjectcontainer(sb, oc); |
| 402 |
|
| 403 |
if ((u8 *) oc + sb->s_blocksize - emptyspace >= bytesneeded) { |
| 404 |
/* We found enough space in one of the ObjectContainer blocks!! |
| 405 |
We return a struct fsObject *. */ |
| 406 |
*io_bh = bh; |
| 407 |
*io_o = (struct fsObject *) emptyspace; |
| 408 |
break; |
| 409 |
} |
| 410 |
nextblock = be32_to_cpu(oc->next); |
| 411 |
asfs_brelse(bh); |
| 412 |
} |
| 413 |
|
| 414 |
if (nextblock == 0) { |
| 415 |
u32 newcontblock; |
| 416 |
/* If we get here, we traversed the *entire* directory (ough!) and found no empty |
| 417 |
space large enough for our entry. We allocate new space and add it to this |
| 418 |
directory. */ |
| 419 |
|
| 420 |
if ((errorcode = allocadminspace(sb, &newcontblock)) == 0 && (bh = asfs_getzeroblk(sb, newcontblock))) { |
| 421 |
struct fsObjectContainer *oc = (void *) bh->b_data; |
| 422 |
struct buffer_head *bhnext; |
| 423 |
|
| 424 |
asfs_debug("findobjectspace: No room was found, allocated new block at %u\n", newcontblock); |
| 425 |
|
| 426 |
/* Allocated new block. We will now link it to the START of the directory chain |
| 427 |
so the new free space can be found quickly when more entries need to be added. */ |
| 428 |
|
| 429 |
oc->bheader.id = cpu_to_be32(ASFS_OBJECTCONTAINER_ID); |
| 430 |
oc->bheader.ownblock = cpu_to_be32(newcontblock); |
| 431 |
oc->parent = oparent->objectnode; |
| 432 |
oc->next = oparent->object.dir.firstdirblock; |
| 433 |
oc->previous = 0; |
| 434 |
|
| 435 |
oparent->object.dir.firstdirblock = cpu_to_be32(newcontblock); |
| 436 |
|
| 437 |
asfs_bstore(sb, bhparent); |
| 438 |
|
| 439 |
if (oc->next != 0 && (bhnext = asfs_breadcheck(sb, be32_to_cpu(oc->next), ASFS_OBJECTCONTAINER_ID))) { |
| 440 |
struct fsObjectContainer *ocnext = (void *) bhnext->b_data; |
| 441 |
ocnext->previous = cpu_to_be32(newcontblock); |
| 442 |
asfs_bstore(sb, bhnext); |
| 443 |
asfs_brelse(bhnext); |
| 444 |
} |
| 445 |
|
| 446 |
*io_bh = bh; |
| 447 |
*io_o = oc->object; |
| 448 |
} |
| 449 |
} |
| 450 |
|
| 451 |
asfs_debug("findobjectspace: new object will be in container block %u\n", be32_to_cpu(((struct fsBlockHeader *) (*io_bh)->b_data)->ownblock)); |
| 452 |
|
| 453 |
return (errorcode); |
| 454 |
} |
| 455 |
|
| 456 |
/* io_bh & io_o refer to the direct parent of the new object. Objectname is the |
| 457 |
name of the new object (name only). Does not realese io_bh !!! */ |
| 458 |
|
| 459 |
int createobject(struct super_block *sb, struct buffer_head **io_bh, struct fsObject **io_o, struct fsObject *src_o, u8 *objectname, int force) |
| 460 |
{ |
| 461 |
int errorcode; |
| 462 |
u32 object_size; |
| 463 |
u32 hashblock = be32_to_cpu((*io_o)->object.dir.hashtable); |
| 464 |
|
| 465 |
asfs_debug("createobject: Creating object '%s' in dir '%s'.\n", objectname, (*io_o)->name); |
| 466 |
|
| 467 |
if (!force && ASFS_SB(sb)->freeblocks < ASFS_ALWAYSFREE) |
| 468 |
return -ENOSPC; |
| 469 |
|
| 470 |
if (!force && be32_to_cpu((*io_o)->objectnode) == ASFS_RECYCLEDNODE) |
| 471 |
return -EINVAL; |
| 472 |
|
| 473 |
object_size = sizeof(struct fsObject) + strlen(objectname) + 2; |
| 474 |
|
| 475 |
if ((errorcode = findobjectspace(sb, io_bh, io_o, object_size)) == 0) { |
| 476 |
struct fsObject *o2 = *io_o; |
| 477 |
u8 *name = o2->name; |
| 478 |
u8 *objname = objectname; |
| 479 |
struct buffer_head *node_bh; |
| 480 |
struct fsObjectNode *on; |
| 481 |
u32 nodeno; |
| 482 |
|
| 483 |
**io_o = *src_o; /* Copying whole object data... */ |
| 484 |
|
| 485 |
while (*objname != 0) /* Copying name */ |
| 486 |
*name++ = *objname++; |
| 487 |
|
| 488 |
*name++ = 0; |
| 489 |
*name = 0; /* zero byte for comment */ |
| 490 |
|
| 491 |
if (o2->objectnode != 0) /* ObjectNode reuse or creation */ |
| 492 |
errorcode = getnode(sb, o2->objectnode, &node_bh, &on); |
| 493 |
else { |
| 494 |
if ((errorcode = createnode(sb, &node_bh, (struct fsNode **) &on, &nodeno)) == 0) { |
| 495 |
on->hash16 = cpu_to_be16(asfs_hash(o2->name, ASFS_SB(sb)->flags & ASFS_ROOTBITS_CASESENSITIVE)); |
| 496 |
o2->objectnode = cpu_to_be32(nodeno); |
| 497 |
} |
| 498 |
asfs_debug("createnode returned with errorcode: %d\n", errorcode); |
| 499 |
} |
| 500 |
|
| 501 |
if (errorcode == 0) { /* in io_bh there is a container with created object */ |
| 502 |
on->node.data = ((struct fsBlockHeader *) (*io_bh)->b_data)->ownblock; |
| 503 |
if ((errorcode = hashobject(sb, hashblock, on, be32_to_cpu(o2->objectnode), objectname)) == 0) { |
| 504 |
asfs_bstore(sb, node_bh); |
| 505 |
asfs_brelse(node_bh); |
| 506 |
} else |
| 507 |
errorcode = -EIO; |
| 508 |
} |
| 509 |
|
| 510 |
if (errorcode == 0) { /* HashBlock reuse or creation:*/ |
| 511 |
|
| 512 |
if ((o2->bits & OTYPE_DIR) != 0 && o2->object.dir.hashtable == 0) { |
| 513 |
struct buffer_head *hashbh; |
| 514 |
u32 hashblock; |
| 515 |
|
| 516 |
asfs_debug("creating Hashblock\n"); |
| 517 |
|
| 518 |
if ((errorcode = allocadminspace(sb, &hashblock)) == 0 && (hashbh = asfs_getzeroblk(sb, hashblock))) { |
| 519 |
struct fsHashTable *ht = (void *) hashbh->b_data; |
| 520 |
|
| 521 |
o2->object.dir.hashtable = cpu_to_be32(hashblock); |
| 522 |
|
| 523 |
ht->bheader.id = cpu_to_be32(ASFS_HASHTABLE_ID); |
| 524 |
ht->bheader.ownblock = cpu_to_be32(hashblock); |
| 525 |
ht->parent = o2->objectnode; |
| 526 |
|
| 527 |
asfs_bstore(sb, hashbh); |
| 528 |
asfs_brelse(hashbh); |
| 529 |
} |
| 530 |
} |
| 531 |
} |
| 532 |
|
| 533 |
if (errorcode == 0) { /* SoftLink creation: */ |
| 534 |
if ((o2->bits & (OTYPE_LINK | OTYPE_HARDLINK)) == OTYPE_LINK && o2->object.file.data == 0) { |
| 535 |
struct buffer_head *bh2; |
| 536 |
u32 slinkblock; |
| 537 |
|
| 538 |
if ((errorcode = allocadminspace(sb, &slinkblock)) == 0 && (bh2 = asfs_getzeroblk(sb, slinkblock))) { |
| 539 |
struct fsSoftLink *sl = (void *) bh2->b_data; |
| 540 |
o2->object.file.data = cpu_to_be32(slinkblock); |
| 541 |
sl->bheader.id = cpu_to_be32(ASFS_SOFTLINK_ID); |
| 542 |
sl->bheader.ownblock = cpu_to_be32(slinkblock); |
| 543 |
sl->parent = o2->objectnode; |
| 544 |
sl->next = 0; |
| 545 |
sl->previous = 0; |
| 546 |
asfs_bstore(sb, bh2); |
| 547 |
asfs_brelse(bh2); |
| 548 |
} |
| 549 |
} |
| 550 |
} |
| 551 |
} |
| 552 |
asfs_debug("createobject: done.\n"); |
| 553 |
|
| 554 |
return (errorcode); |
| 555 |
} |
| 556 |
|
| 557 |
/* This function extends the file object 'o' with a number of blocks |
| 558 |
(hopefully, if any blocks has been found!). Only new Extents will |
| 559 |
be created -- the size of the file will not be altered, and changing |
| 560 |
it is left up to the caller. If the file did not have any blocks |
| 561 |
yet, then the o->object.file.data will be set to the first (new) |
| 562 |
ExtentBNode. It returns the number of added blocks through |
| 563 |
addedblocks pointer */ |
| 564 |
|
| 565 |
int addblockstofile(struct super_block *sb, struct buffer_head *objbh, struct fsObject *o, u32 blocks, u32 * newspace, u32 * addedblocks) |
| 566 |
{ |
| 567 |
u32 lastextentbnode; |
| 568 |
int errorcode = 0; |
| 569 |
struct fsExtentBNode *ebnp; |
| 570 |
struct buffer_head *block = NULL; |
| 571 |
|
| 572 |
|
| 573 |
asfs_debug("extendblocksinfile: Trying to increasing number of blocks by %d.\n", blocks); |
| 574 |
|
| 575 |
lastextentbnode = be32_to_cpu(o->object.file.data); |
| 576 |
|
| 577 |
if (lastextentbnode != 0) { |
| 578 |
while (lastextentbnode != 0 && errorcode == 0) { |
| 579 |
if (block != NULL) |
| 580 |
asfs_brelse(block); |
| 581 |
errorcode = getextent(sb, lastextentbnode, &block, &ebnp); |
| 582 |
lastextentbnode = be32_to_cpu(ebnp->next); |
| 583 |
} |
| 584 |
lastextentbnode = be32_to_cpu(ebnp->key); |
| 585 |
} |
| 586 |
|
| 587 |
if (errorcode == 0) { |
| 588 |
u32 searchstart; |
| 589 |
|
| 590 |
u32 found_block; |
| 591 |
u32 found_blocks; |
| 592 |
|
| 593 |
*addedblocks = 0; |
| 594 |
*newspace = 0; |
| 595 |
|
| 596 |
if (lastextentbnode != 0) |
| 597 |
searchstart = be32_to_cpu(ebnp->key) + be16_to_cpu(ebnp->blocks); |
| 598 |
else |
| 599 |
searchstart = 0; //ASFS_SB(sb)->block_rovingblockptr; |
| 600 |
|
| 601 |
if ((errorcode = findspace(sb, blocks, searchstart, searchstart, &found_block, &found_blocks)) != 0) { |
| 602 |
asfs_brelse(block); |
| 603 |
asfs_debug("extendblocksinfile: findspace returned %s\n", errorcode == -ENOSPC ? "ENOSPC" : "error"); |
| 604 |
return errorcode; |
| 605 |
} |
| 606 |
|
| 607 |
blocks = found_blocks; |
| 608 |
errorcode = markspace(sb, found_block, found_blocks); |
| 609 |
*addedblocks = found_blocks; |
| 610 |
*newspace = found_block; |
| 611 |
|
| 612 |
asfs_debug("extendblocksinfile: block = %u, lastextentbnode = %u, extentblocks = %d\n", found_block, lastextentbnode, blocks); |
| 613 |
|
| 614 |
if ((errorcode = addblocks(sb, blocks, found_block, be32_to_cpu(o->objectnode), &lastextentbnode)) != 0) { |
| 615 |
asfs_debug("extendblocksinfile: addblocks returned errorcode %d\n", errorcode); |
| 616 |
return errorcode; |
| 617 |
} |
| 618 |
|
| 619 |
if (o->object.file.data == 0) |
| 620 |
o->object.file.data = cpu_to_be32(lastextentbnode); |
| 621 |
} |
| 622 |
|
| 623 |
if (block) |
| 624 |
asfs_brelse(block); |
| 625 |
asfs_bstore(sb, objbh); |
| 626 |
|
| 627 |
asfs_debug("addblockstofile: done. added %d blocks\n", *addedblocks); |
| 628 |
|
| 629 |
return errorcode; |
| 630 |
} |
| 631 |
|
| 632 |
/* The Object indicated by bh1 & o1, gets renamed to newname and placed |
| 633 |
in the directory indicated by bhparent & oparent. */ |
| 634 |
|
| 635 |
int renameobject(struct super_block *sb, struct buffer_head *bh1, struct fsObject *o1, struct buffer_head *bhparent, struct fsObject *oparent, u8 * newname) |
| 636 |
{ |
| 637 |
struct fsObject object; |
| 638 |
u32 oldparentnode = be32_to_cpu(((struct fsObjectContainer *) bh1->b_data)->parent); |
| 639 |
u8 oldname[107]; |
| 640 |
int errorcode; |
| 641 |
|
| 642 |
asfs_debug("renameobject: Renaming '%s' to '%s' in dir '%s'\n", o1->name, newname, oparent->name); |
| 643 |
|
| 644 |
object = *o1; |
| 645 |
strcpy(oldname, o1->name); |
| 646 |
|
| 647 |
if ((errorcode = dehashobjectquick(sb, be32_to_cpu(o1->objectnode), o1->name, oldparentnode)) == 0) { |
| 648 |
u32 parentobjectnode = be32_to_cpu(oparent->objectnode); |
| 649 |
|
| 650 |
if ((errorcode = simpleremoveobject(sb, bh1, o1)) == 0) { |
| 651 |
struct buffer_head *bh2 = bhparent; |
| 652 |
struct fsObject *o2; |
| 653 |
|
| 654 |
/* oparent might changed after simpleremoveobject */ |
| 655 |
oparent = o2 = find_obj_by_node(sb, (struct fsObjectContainer *) bhparent->b_data, parentobjectnode); |
| 656 |
|
| 657 |
/* In goes the Parent bh & o, out comes the New object's bh & o :-) */ |
| 658 |
if ((errorcode = createobject(sb, &bh2, &o2, &object, newname, TRUE)) == 0) { |
| 659 |
asfs_bstore(sb, bh2); |
| 660 |
if (be32_to_cpu(oparent->objectnode) == ASFS_RECYCLEDNODE) { |
| 661 |
asfs_debug("renameobject: Updating recycled dir info\n"); |
| 662 |
if ((errorcode = setrecycledinfodiff(sb, 1, (be32_to_cpu(o2->object.file.size) + sb->s_blocksize - 1) >> sb->s_blocksize_bits)) != 0) { |
| 663 |
brelse(bh2); |
| 664 |
return errorcode; |
| 665 |
} |
| 666 |
} |
| 667 |
brelse(bh2); |
| 668 |
asfs_debug("renameobject: Succesfully created & stored new object.\n"); |
| 669 |
} else { /* recreate object in old place, maybe this will not fail, but who knows... */ |
| 670 |
asfs_debug("renameobject: Creating new object failed. Trying to recreate it in source directory.\n"); |
| 671 |
if (readobject(sb, oldparentnode, &bh1, &o1) == 0) { |
| 672 |
struct buffer_head *bh2 = bh1; |
| 673 |
if (createobject(sb, &bh2, &o1, &object, oldname, TRUE) == 0) { |
| 674 |
asfs_bstore(sb, bh2); |
| 675 |
if (oldparentnode == ASFS_RECYCLEDNODE) { |
| 676 |
asfs_debug("renameobject: Updating recycled dir info\n"); |
| 677 |
setrecycledinfodiff(sb, 1, (be32_to_cpu(o1->object.file.size) + sb->s_blocksize - 1) >> sb->s_blocksize_bits); |
| 678 |
} |
| 679 |
brelse(bh2); |
| 680 |
} |
| 681 |
brelse(bh1); |
| 682 |
} |
| 683 |
} |
| 684 |
} |
| 685 |
} |
| 686 |
return errorcode; |
| 687 |
} |
| 688 |
|
| 689 |
/* Truncates the specified file to /newsize/ bytes */ |
| 690 |
|
| 691 |
int truncateblocksinfile(struct super_block *sb, struct buffer_head *bh, struct fsObject *o, u32 newsize) |
| 692 |
{ |
| 693 |
struct buffer_head *ebh; |
| 694 |
struct fsExtentBNode *ebn; |
| 695 |
int errorcode; |
| 696 |
u32 pos = 0; |
| 697 |
u32 newblocks = (newsize + sb->s_blocksize - 1) >> sb->s_blocksize_bits; |
| 698 |
u32 filedata = be32_to_cpu(o->object.file.data); |
| 699 |
u32 eprev, ekey; |
| 700 |
u16 eblocks; |
| 701 |
|
| 702 |
asfs_debug("trucateblocksinfile: newsize %u\n", newsize); |
| 703 |
|
| 704 |
if (filedata == 0) |
| 705 |
return 0; |
| 706 |
|
| 707 |
for (;;) { |
| 708 |
if ((errorcode = getextent(sb, filedata, &ebh, &ebn)) != 0) |
| 709 |
return errorcode; |
| 710 |
if (pos + be16_to_cpu(ebn->blocks) >= newblocks) |
| 711 |
break; |
| 712 |
pos += be16_to_cpu(ebn->blocks); |
| 713 |
if ((filedata = be32_to_cpu(ebn->next)) == 0) |
| 714 |
break; |
| 715 |
asfs_brelse(ebh); |
| 716 |
}; |
| 717 |
|
| 718 |
eblocks = newblocks - pos; |
| 719 |
ekey = be32_to_cpu(ebn->key); |
| 720 |
eprev = be32_to_cpu(ebn->prev); |
| 721 |
|
| 722 |
if (be16_to_cpu(ebn->blocks) < eblocks) { |
| 723 |
printk("ASFS: Extent chain is too short or damaged!\n"); |
| 724 |
asfs_brelse(ebh); |
| 725 |
return -ENOENT; |
| 726 |
} |
| 727 |
if (be16_to_cpu(ebn->blocks) - eblocks > 0 && (errorcode = freespace(sb, be32_to_cpu(ebn->key) + eblocks, be16_to_cpu(ebn->blocks) - eblocks)) != 0) { |
| 728 |
asfs_brelse(ebh); |
| 729 |
return errorcode; |
| 730 |
} |
| 731 |
if (be32_to_cpu(ebn->next) > 0 && (errorcode = deleteextents(sb, be32_to_cpu(ebn->next))) != 0) { |
| 732 |
asfs_brelse(ebh); |
| 733 |
return errorcode; |
| 734 |
} |
| 735 |
ebn->blocks = cpu_to_be16(eblocks); |
| 736 |
ebn->next = 0; |
| 737 |
asfs_bstore(sb, ebh); |
| 738 |
|
| 739 |
if (eblocks == 0) { |
| 740 |
if (eprev & MSB_MASK) { |
| 741 |
o->object.file.data = 0; |
| 742 |
asfs_bstore(sb, bh); |
| 743 |
} else { |
| 744 |
struct buffer_head *ebhp; |
| 745 |
struct fsExtentBNode *ebnp; |
| 746 |
|
| 747 |
if ((errorcode = getextent(sb, eprev & !MSB_MASK, &ebhp, &ebnp)) != 0) { |
| 748 |
asfs_brelse(ebh); |
| 749 |
return errorcode; |
| 750 |
} |
| 751 |
|
| 752 |
ebnp->next = 0; |
| 753 |
asfs_bstore(sb, ebhp); |
| 754 |
asfs_brelse(ebhp); |
| 755 |
} |
| 756 |
if ((errorcode = deletebnode(sb, ebh, ekey)) != 0) { |
| 757 |
asfs_brelse(ebh); |
| 758 |
return errorcode; |
| 759 |
} |
| 760 |
} |
| 761 |
asfs_brelse(ebh); |
| 762 |
|
| 763 |
return 0; |
| 764 |
} |
| 765 |
#endif |