tools/adflib: build only host variant which is used by Sam440 target
[AROS.git] / rom / filesys / pfs3 / fs / directory.c
blobb45e81ec1f1f6577955db939d0f6a3ad2257ce11
1 /* $Id$ */
2 /* $Log: directory.c $
3 * Revision 15.21 1999/09/11 17:05:14 Michiel
4 * bugfix version 18.4
6 * Revision 15.20 1999/05/14 11:31:34 Michiel
7 * Long filename support implemented; bugfixes
9 * Revision 15.19 1999/05/10 23:53:29 Michiel
10 * hardlink bug (ST_LINKFILE/ST_LINKDIR) fixed
12 * Revision 15.18 1999/04/25 21:48:19 Michiel
13 * bug 00141 fixed: hardlink overwrite problem in NewFile()
15 * Revision 15.17 1999/04/23 11:12:39 Michiel
16 * optimisation: delfile is deleted when detected to be empty (during dirscan)
18 * Revision 15.16 1999/03/23 06:00:19 Michiel
19 * More informative error messages in setdeldir
20 * Maxdeldir bug in setdeldir fixed
22 * Revision 15.15 1999/03/09 10:34:02 Michiel
23 * typo in mufs code
25 * Revision 15.14 1999/02/22 16:31:55 Michiel
26 * Changes for increasing deldir capacity
27 * Fixed link problems; new function MoveLink
29 * Revision 15.13 1998/09/27 11:26:37 Michiel
30 * ErrorMsg param
32 * Revision 15.12 1998/09/03 07:12:14 Michiel
33 * versie 17.4
34 * bugfixes 118, 121, 123 and superindexblocks and td64 support
36 * Revision 15.11 1997/03/03 22:04:04 Michiel
37 * Release 16.21
39 * Revision 15.10 1996/04/22 22:12:41 Michiel
40 * NewFile overwrite bug fix
41 * SetDate deldirfile bug fix
43 * Revision 15.9 1996/03/14 19:30:52 Michiel
44 * The connect anode for directory anode allocation now is the second anode,
45 * not the head anode --> better large directory performance
46 * NewFile does not call SearchInDir anymore --> better (large) dir perf.
47 * (see log 15.3.96)
49 * Revision 15.8 1996/03/07 10:09:09 Michiel
50 * rename bug fixed (wt)
52 * Revision 15.7 1996/01/30 12:47:46 Michiel
53 * --- working tree overlap ---
54 * Softlink bug fixes
55 * Notify-update changes
57 * Revision 15.6 1995/12/29 11:02:43 Michiel
58 * SetRollover extended
60 * Revision 15.4 1995/12/21 12:00:51 Michiel
61 * bugfix: newfile didn't clear extrafields/filetype
62 * Now using FreeBlockAC directly instead of buggy FreeAnodeBlocksAC
64 * Revision 15.3 1995/12/20 11:54:19 Michiel
65 * indented
67 * Revision 15.2 1995/12/07 15:25:38 Michiel
68 * rollover support and bugfixes
70 * Revision 15.1 1995/11/15 15:41:17 Michiel
71 * Postponed operation support:
72 * AllocDeldirSlot, AddToDeldir, FreeAnodeBlocks, NewFile, DeleteObject, FreeAnodesInChain
73 * Rootblock extension: dates in Touch, Examine
75 * Revision 14.12 1995/11/07 14:54:13 Michiel
76 * Checks for ReservedAreaLock where needed
78 * Revision 14.11 1995/10/20 10:08:37 Michiel
79 * Anode reserved area adaptions (16.3)
81 * Revision 14.10 1995/10/04 08:58:17 Michiel
82 * FreeAnodeBlocks nu met anodechains geimplementeerd (FAB kan nu falen!).
84 * Revision 14.9 1995/10/03 10:59:37 Michiel
85 * merged
87 * Revision 14.8 1995/09/28 12:18:25 Michiel
88 * soft/hardlink namelength check added
89 * Deldir now frees oldblocknr of dirty member blocks
91 * Revision 14.7 1995/08/24 13:01:27 Michiel
92 * Touch now doesn't touch rootblock because that gives
93 * disk recognition problems when changing disks
95 * Revision 14.6 1995/08/21 04:18:49 Michiel
96 * RenameAndMove didn't check if source was deldir
98 * Revision 14.5 1995/08/16 14:31:34 Michiel
99 * added KillEmpty
100 * added forced_RemoveDirEntry
102 * Revision 14.4 1995/08/04 04:17:09 Michiel
103 * SetProtection on deldir now allowed (using masks).
104 * SetOwner on deldir now allowed
105 * Touching deldir when file added to deldir
106 * Now using protection field of deldir for all deldirentries
108 * Revision 14.3 1995/08/02 16:09:39 Michiel
109 * Filename size limitation (31 characters)
110 * Empty filenames not accepted
112 * Revision 14.2 1995/07/28 07:58:01 Michiel
113 * Deldirentry valid check added
115 * Revision 14.1 1995/07/21 06:43:33 Michiel
116 * DELDIR implemented
117 * fixed ExNext problem (?)
118 * NewDir now does a diskwp. check
119 * Touch() now updates rootdir too
121 * Revision 13.11 1995/07/11 17:29:31 Michiel
122 * ErrorMsg () calls use messages.c variables now.
124 * Revision 13.10 1995/07/11 09:23:36 Michiel
125 * DELDIR stuff
127 * Revision 13.9 1995/07/07 14:41:21 Michiel
128 * no fib->fib_Reserved stuff
129 * ProtectFile fix
131 * Revision 13.8 1995/06/23 17:26:57 Michiel
132 * MakeDirEntry now also sets protection to default and owner to current (multiuser)
134 * Revision 13.7 1995/06/21 08:13:18 Michiel
135 * CheckVolume() calls removed (moved to dostohandlerinterface.c)
137 * Revision 13.6 1995/06/20 18:55:00 Michiel
138 * fixed a bug in FreeAnodeBlocks
139 * Examine checks softprotect
141 * Revision 13.5 1995/06/15 18:55:28 Michiel
142 * pooled mem
144 * Revision 13.4 1995/06/08 11:12:32 Michiel
145 * Now GetFullPath checks if path is directory.
147 * Revision 13.3 1995/06/04 07:43:54 Michiel
148 * Longword protection implemented
150 * Revision 13.2 1995/05/03 19:14:13 Michiel
151 * LOCK added to create soft & hardlink
153 * Revision 13.1 1995/04/23 09:06:53 Michiel
154 * Rewritten direntry change routines,
155 * solving directory-order problem
157 * Revision 12.9 1995/04/18 17:08:16 Michiel
158 * CopyMem() to memcpy() and some cosmetic changes
160 * Revision 12.8 1995/04/14 10:03:53 Michiel
161 * 'Out of buffers' problem fixed.
163 * Revision 12.7 1995/04/05 12:24:43 Michiel
164 * Bugfix in ExamineAll
165 * Don't allow hardlinks when no dirextension
167 * Revision 12.6 1995/03/30 11:53:46 Michiel
168 * Rename now does a notify to sourcedir if rename across dirs
170 * Revision 12.5 1995/03/30 10:50:37 Michiel
171 * some notify stuff added
173 * Revision 12.4 1995/03/24 16:29:57 Michiel
174 * Softlink support added
175 * Hardlink bugs fixed
177 * Revision 12.3 1995/02/28 18:20:49 Michiel
178 * Link handling functions added: DeleteLink(), RemapLinks(), UpdateLinks(),
179 * FetchObject(), UpdateLinkDir().
180 * NewFile() takes over old direntry now, instead of delete/create.
181 * GetDir() resolves links.
183 * Revision 12.2 1995/02/24 07:12:41 Michiel
184 * CreateLink added, MakeDirEntry changed
186 * Revision 12.1 1995/02/23 06:57:01 Michiel
187 * Directory extension implemented
189 * Revision 11.5 1995/02/15 16:43:39 Michiel
190 * Release version
191 * Using new headers (struct.h & blocks.h)
193 * Revision 11.4 1995/01/29 07:34:57 Michiel
194 * Bug in FreeAnodeBlocks fixed
196 * Revision 11.3 1995/01/24 09:49:51 Michiel
197 * Cache hashing added
199 * Revision 11.2 1995/01/18 04:29:34 Michiel
200 * Bugfixes. Now ready for beta release.
202 * Revision 11.1 1995/01/08 16:16:29 Michiel
203 * Compiled
205 * Revision 10.4 1994/11/15 17:52:30 Michiel
206 * GuruBook update
208 * Revision 10.3 1994/10/28 06:06:40 Michiel
209 * uses new listentry field anodenr
211 * Revision 10.2 1994/10/27 11:29:33 Michiel
212 * *** empty log message ***
214 * Revision 10.1 1994/10/24 11:16:28 Michiel
215 * first RCS revision
216 * */
218 // anodenr in cdirblock !!! @->bugnr @@->currently wrong
219 //#define DEBUG 1
220 #define __USE_SYSBASE
222 #include <exec/types.h>
223 #include <exec/memory.h>
224 #include <exec/devices.h>
225 #include <exec/io.h>
226 #include <dos/exall.h>
227 #include <dos/filehandler.h>
228 #include <proto/intuition.h>
229 #include <proto/utility.h>
230 #if MULTIUSER
231 #include <proto/multiuser.h>
232 #endif
233 #include <intuition/intuition.h>
234 #include <intuition/intuitionbase.h>
235 #include <string.h>
236 #include <stdlib.h>
237 #include <stdio.h>
238 #include <math.h>
239 #include <ctype.h>
240 #include <limits.h>
242 // own includes
243 #include "debug.h"
244 #include "blocks.h"
245 #include "struct.h"
246 #include "directory_protos.h"
247 #include "allocation_protos.h"
248 #include "volume_protos.h"
249 #include "lock_protos.h"
250 #include "disk_protos.h"
251 #include "anodes_protos.h"
252 #include "update_protos.h"
253 #include "lru_protos.h"
254 #include "ass_protos.h"
255 #include "support.c"
257 void PFSDoNotify(struct fileinfo *object, BOOL checkparent, globaldata * g);
258 void PFSUpdateNotify(ULONG dirnode, DSTR filename, ULONG anodenr, globaldata * g);
260 /**********************************************************************/
261 /* DEBUG */
262 /**********************************************************************/
264 #ifdef DEBUG
265 extern BOOL debug;
266 static UBYTE debugbuf[120];
267 #define DebugOn debug++
268 #define DebugOff debug=0
269 #define DebugMsg(msg) if(debug) {NormalErrorMsg(msg, NULL);debug=0;}
270 #define DebugMsgNum(msg, num) sprintf(debugbuf, "%s 0x%08lx.", msg, num); \
271 if(debug) {NormalErrorMsg(debugbuf, NULL);debug=0;}
272 #define DebugMsgName(msg, name) sprintf(debugbuf, "%s >%s<.", msg, name); \
273 if(debug) {NormalErrorMsg(debugbuf, NULL);debug=0;}
274 #else
275 #define DebugOn
276 #define DebugOff
277 #define DebugMsg(m)
278 #define DebugMsgNum(msg,num)
279 #define DebugMsgName(msg, name)
280 #endif
283 * Contents
285 static BOOL GetParentOf(union objectinfo *path, SIPTR *error, globaldata *);
286 static BOOL GetDir(STRPTR dirname, union objectinfo *path, SIPTR *error, globaldata *);
287 static BOOL GetObject(STRPTR objectname, union objectinfo *path, SIPTR *error, globaldata *);
288 static BOOL SearchInDir(ULONG dirnodenr, STRPTR objectname, union objectinfo *info, globaldata * g);
289 static void FillFib(struct direntry *direntry, struct FileInfoBlock *fib, globaldata * g);
290 #if DELDIR
291 static struct deldirentry *SearchInDeldir(STRPTR delname, union objectinfo *result, globaldata *g);
292 static BOOL IsDelfileValid(struct deldirentry *dde, struct cdeldirblock *ddblk, globaldata *g);
293 static BOOL BlockTaken(struct canode *anode, globaldata *g);
294 static void FillDelfileFib(struct deldirentry *dde, ULONG slotnr, struct FileInfoBlock *fib, globaldata *g);
295 static struct deldirentry *GetDeldirEntry(IPTR *nr, globaldata *g);
296 static ULONG FillInDDEData(struct ExAllDataEXT *buffer, LONG type, struct deldirentry *dde, ULONG ddenr, ULONG spaceleft, globaldata *g);
297 static struct cdeldirblock *GetDeldirBlock(UWORD seqnr, globaldata *g);
298 #endif
299 static void GetFirstEntry(lockentry_t *, globaldata *);
300 static ULONG GetFirstNonEmptyDE(ULONG anodenr, struct fileinfo *, globaldata *);
301 static void RemakeNextEntry(lockentry_t *, struct FileInfoBlock *, globaldata *);
302 static ULONG FillInData(struct ExAllDataEXT *, LONG, struct direntry *, ULONG, globaldata *);
303 static BOOL DeleteDir(union objectinfo *, SIPTR *, globaldata *);
304 static BOOL DirIsEmpty(ULONG, globaldata *);
305 static BOOL MakeDirEntry(BYTE type, UBYTE *name, UBYTE *entrybuffer, globaldata * g);
306 static BOOL IsChildOf(union objectinfo child, union objectinfo parent, globaldata * g);
307 static BOOL DeleteLink(struct fileinfo *link, SIPTR *, globaldata * g);
308 static BOOL RemapLinks(struct fileinfo *object, globaldata * g);
309 static void UpdateLinkDir(struct direntry *object, ULONG newdiran, globaldata * g);
310 static void MoveLink(struct direntry *object, ULONG newdiran, globaldata *g);
311 static void RenameAcrossDirs(struct fileinfo from, struct direntry *to, union objectinfo *destdir, struct fileinfo *result, globaldata * g);
312 static void RenameWithinDir(struct fileinfo from, struct direntry *to, struct fileinfo *newinfo, globaldata * g);
313 static void RenameInPlace(struct fileinfo from, struct direntry *to, struct fileinfo *result, globaldata * g);
314 static struct direntry *CheckFit(struct cdirblock *blok, int needed, globaldata * g);
315 static BOOL MoveToPrevious(struct fileinfo de, struct direntry *to, struct fileinfo *result, globaldata * g);
316 static void RemoveDirEntry(struct fileinfo info, globaldata * g);
317 static BOOL AddDirectoryEntry(union objectinfo *dir, struct direntry *newentry, struct fileinfo *newinfo, globaldata * g);
318 static void UpdateChangedRef(struct fileinfo from, struct fileinfo *to, int diff, globaldata * g);
320 #if VERSION23
321 static int AllocDeldirSlot(globaldata * g);
322 static void AddToDeldir(union objectinfo *info, int ddnr, globaldata * g);
323 #else
324 static void AddToDeldir(union objectinfo *info, globaldata * g);
325 static BOOL FreeAnodeBlocks(ULONG anodenr, enum freeblocktype freenodes, globaldata * g);
326 #endif
328 /**********************************************************************/
329 /* SEARCH IN DIRECTORY */
330 /* SEARCH IN DIRECTORY */
331 /* SEARCH IN DIRECTORY */
332 /**********************************************************************/
334 /* GetFullPath converts a relative path to an absolute path.
335 * The fileinfo of the new path is returned in [result].
336 * The return value is the filename without path.
337 * Error: return 0
339 * Parsing Syntax:
340 * : after '/' or at the beginning ==> root
341 * : after [name] ==> volume [name]
342 * / after / or ':' or at the beginning ==> parent
343 * / after dir ==> get dir
344 * / after file ==> error (ALWAYS) (AMIGADOS ok if LAST file)
346 * IN basispath, filename, g
347 * OUT fullpath, error
349 * If only a partial path is found, a pointer to the unparsed part
350 * will be stored in g->unparsed.
352 UBYTE *GetFullPath(union objectinfo *basispath, STRPTR filename,
353 union objectinfo *fullpath, SIPTR *error, globaldata * g)
355 UBYTE *pathpart, parttype;
356 COUNT index;
357 BOOL eop = FALSE, success = TRUE;
358 struct volumedata *volume;
360 // VVV Init:getrootvolume
361 ENTER("GetFullPath");
362 g->unparsed = NULL;
364 /* Set base path */
365 if (basispath)
366 *fullpath = *basispath;
367 else
368 GetRoot(fullpath, g);
370 /* The basispath should not be a file
371 * BTW: softlink is illegal too, but not possible
373 if (IsFile(*fullpath) || IsDelFile(*fullpath))
375 *error = ERROR_OBJECT_WRONG_TYPE;
376 return NULL;
379 /* check if device present */
380 if (IsVolume(*fullpath) || IsDelDir(*fullpath))
381 volume = fullpath->volume.volume;
382 else
383 volume = fullpath->file.dirblock->volume;
385 if (!CheckVolume(volume, 0, error, g))
386 return (0);
388 /* extend base-path using filename and
389 * continue until path complete (eop = end of path)
391 while (!eop)
393 pathpart = filename;
394 index = strcspn(filename, "/:");
395 parttype = filename[index];
396 filename[index] = 0x0;
398 switch (parttype)
400 case ':':
401 success = FALSE;
402 break;
404 case '/':
405 if (*pathpart == 0x0)
406 success = GetParentOf(fullpath, error, g);
407 else
408 success = GetDir(pathpart, fullpath, error, g);
409 break;
411 default:
412 eop = TRUE;
415 filename[index] = parttype;
417 if (!success)
419 /* return pathrest for readlink() */
420 if (*error == ERROR_IS_SOFT_LINK)
421 g->unparsed = filename + index;
422 else if (*error == ERROR_OBJECT_NOT_FOUND)
423 g->unparsed = filename;
424 return NULL;
427 if (!eop)
428 filename += index + 1;
431 return filename;
434 BOOL GetRoot(union objectinfo * path, globaldata * g)
436 UpdateCurrentDisk(g);
437 path->volume.root = 0;
438 path->volume.volume = g->currentvolume;
439 return DOSTRUE;
442 /* pre: - path <> 0 and volume or directory
443 * result back in path
445 static BOOL GetParentOf(union objectinfo *path, SIPTR *error, globaldata * g)
447 BOOL ok;
448 union objectinfo info;
450 info = *path;
451 ok = GetParent(&info, path, error, g);
452 return ok;
455 /* pre: - path <> 0 and volume of directory
456 * - dirname without path; strlen(dirname) > 0
457 * result back in path
459 static BOOL GetDir(STRPTR dirname, union objectinfo *path, SIPTR *error, globaldata * g)
461 BOOL found;
463 found = GetObject(dirname, path, error, g);
465 #if DELDIR
466 if (g->deldirenabled && IsDelDir(*path))
467 return DOSTRUE;
468 #endif
470 /* check if found directory */
471 #if DELDIR
472 if (!found || IsFile(*path) || IsDelFile(*path))
473 #else
474 if (!found || IsFile(*path))
475 #endif
477 *error = ERROR_OBJECT_NOT_FOUND; // DOPUS doesn't like DIR_NOT_FOUND
478 return DOSFALSE;
481 /* check if softlink */
482 if (IsSoftLink(*path))
484 *error = ERROR_IS_SOFT_LINK;
485 return DOSFALSE;
488 /* resolve links */
489 if (path->file.direntry->type == ST_LINKDIR)
491 struct extrafields extrafields;
492 struct canode linknode;
494 GetExtraFields(path->file.direntry, &extrafields);
495 GetAnode(&linknode, path->file.direntry->anode, g);
496 if (!FetchObject(linknode.clustersize, extrafields.link, path, g))
497 return DOSFALSE;
500 return DOSTRUE;
503 /* pre: - path<>0 and volume of directory
504 * - objectname without path; strlen(objectname) > 0
505 * result back in path
507 static BOOL GetObject(STRPTR objectname, union objectinfo *path, SIPTR *error, globaldata *g)
509 ULONG anodenr;
510 BOOL found;
511 #if DELDIR
512 if (IsDelDir(*path))
514 found = (SearchInDeldir(objectname, path, g) != NULL);
515 goto go_error;
517 #endif
519 if (IsVolume(*path))
520 anodenr = ANODE_ROOTDIR;
521 else
522 anodenr = path->file.direntry->anode;
524 DB(Trace(1, "GetObject", "parent anodenr %lx\n", anodenr));
525 found = SearchInDir(anodenr, objectname, path, g);
527 go_error:
528 if (!found)
530 #if DELDIR
531 if (g->deldirenabled && IsVolume(*path))
533 if (stricmp(deldirname+1, objectname) == 0)
535 path->deldir.special = SPECIAL_DELDIR;
536 path->deldir.volume = g->currentvolume;
537 return DOSTRUE;
540 #endif
541 *error = ERROR_OBJECT_NOT_FOUND;
542 return DOSFALSE;
545 return DOSTRUE;
548 /* <FindObject>
550 * FindObject searches the object 'fname' in directory 'directory'.
551 * FindObject zoekt het object 'fname' in directory 'directory'.
552 * Interpret empty filename as parent and ":" as root.
553 * Does not use multiple-assign-list
555 * input : - [directory]: the 'root' directory of the search
556 * - [objectname]: file to be found, including path
558 * output: - [object]: If file found : fileinfo of object
559 * If path found : fileinfo of directory
560 * - [error]: Errornumber as result = DOSFALSE; otherwise 0
562 * result: DOSTRUE (-1) = file found (->in fileinfo)
563 * DOSFALSE (0) = error
565 * If only a partial path is found, a pointer to the unparsed part
566 * will be stored in g->unparsed.
568 BOOL FindObject(union objectinfo *directory, STRPTR objectname,
569 union objectinfo *object, SIPTR *error, globaldata *g)
571 UBYTE *filename;
572 BOOL ok;
574 *error = 0;
575 filename = GetFullPath(directory, objectname, object, error, g);
577 if (!filename)
579 DB(Trace(2, "FindObject", "!filename %s\n", objectname));
580 return DOSFALSE;
583 /* path only (dir or volume) */
584 if (!*filename)
585 return DOSTRUE;
587 /* there is a filepart (file or dir) */
588 ok = GetObject(filename, object, error, g);
589 if (!ok && (*error == ERROR_OBJECT_NOT_FOUND))
590 g->unparsed = filename;
592 return ok;
596 /* GetParent
598 * childanodenr = anodenr of start directory (the child)
599 * parentanodenr = anodenr of directory containing childanodenr (the parent)
600 * childfi == parentfi can be dangerous
601 * in:childfi; out:parentfi, error
603 BOOL GetParent(union objectinfo *childfi, union objectinfo *parentfi, SIPTR *error, globaldata *g)
605 struct canode anode;
606 struct cdirblock *dirblock;
607 struct direntry *de;
608 ULONG anodeoffset = 0;
609 ULONG childanodenr, parentanodenr;
610 BOOL eod = FALSE, eob = FALSE, found = FALSE;
612 // -I- Find anode of parent
613 if (!childfi || IsVolume(*childfi)) // child is rootdir
615 *error = 0x0; /* No error; just return NULL */
616 return 0;
619 #if DELDIR
620 if (g->deldirenabled)
622 if (IsDelDir(*childfi))
623 return GetRoot(parentfi, g);
625 if (IsDelFile(*childfi))
627 parentfi->deldir.special = SPECIAL_DELDIR;
628 parentfi->deldir.volume = g->currentvolume;
629 return TRUE;
632 #endif
634 childanodenr = childfi->file.dirblock->blk.anodenr; /* the directory 'child' is in */
635 parentanodenr = childfi->file.dirblock->blk.parent; /* the directory 'childanodenr' is in */
637 // -II- check if in root
638 if (parentanodenr == 0) /* child is in rootdir */
640 parentfi->volume.root = 0;
641 parentfi->volume.volume = childfi->file.dirblock->volume;
642 return TRUE;
645 // -III- get parentdirectory and find direntry
646 GetAnode(&anode, parentanodenr, g);
647 while (!found && !eod)
649 dirblock = LoadDirBlock(anode.blocknr + anodeoffset, g);
650 if (dirblock)
652 de = FIRSTENTRY(dirblock);
653 eob = 0;
655 while (!found && !eob)
657 found = (de->anode == childanodenr);
658 eob = (de->next == 0);
659 if (!found && !eob)
660 de = NEXTENTRY(de);
663 if (!found)
664 eod = !NextBlock(&anode, &anodeoffset, g);
666 else
668 break;
672 if (!found)
674 DB(Trace(1, "GetParent", "DiskNotValidated %ld\n", childanodenr));
675 *error = ERROR_DISK_NOT_VALIDATED;
676 return FALSE;
679 parentfi->file.direntry = de;
680 parentfi->file.dirblock = dirblock;
681 LOCK(dirblock);
682 return TRUE;
686 /* SearchInDir
688 * Search an object in a directory and return the fileinfo
690 * input : - dirnodenr: anodenr of directory to search in
691 * - objectname: found object (without path)
693 * output: - info: objectinfo of found object
695 * result: success
697 static BOOL SearchInDir(ULONG dirnodenr, STRPTR objectname, union objectinfo *info, globaldata * g)
699 struct canode anode;
700 struct cdirblock *dirblock;
701 struct direntry *entry = NULL;
702 BOOL found = FALSE, eod = FALSE;
703 ULONG anodeoffset;
704 UBYTE intl_name[PATHSIZE];
706 ENTER("SearchInDir");
707 ctodstr(objectname, intl_name);
709 /* truncate */
710 if (intl_name[0] > FILENAMESIZE - 1)
711 intl_name[0] = FILENAMESIZE - 1;
713 intltoupper(intl_name); /* international uppercase objectname */
714 GetAnode(&anode, dirnodenr, g);
715 anodeoffset = 0;
716 dirblock = LoadDirBlock(anode.blocknr, g);
718 while (dirblock && !found && !eod) /* eod stands for end-of-dir */
720 entry = (struct direntry *)(&dirblock->blk.entries);
722 /* scan block */
723 while (entry->next)
725 found = intlcmp(intl_name, DIRENTRYNAME(entry));
726 if (found)
727 break;
728 entry = NEXTENTRY(entry);
731 /* load next block */
732 if (!found)
734 if (NextBlock(&anode, &anodeoffset, g))
735 dirblock = LoadDirBlock(anode.blocknr + anodeoffset, g);
736 else
737 eod = TRUE;
741 /* make fileinfo */
742 if (!dirblock)
744 return FALSE;
746 else if (found)
748 info->file.direntry = entry;
749 info->file.dirblock = dirblock;
750 LOCK(dirblock);
751 return TRUE;
753 else
754 return FALSE;
759 * Search object by anodenr in directory
760 * in: diranodenr, target: anodenr of target and the anodenr of the directory to search in
761 * out: result: an objectinfo to the object, if found
762 * returns: success
764 BOOL FetchObject(ULONG diranodenr, ULONG target, union objectinfo * result, globaldata * g)
766 struct canode anode;
767 struct cdirblock *dirblock;
768 struct direntry *de;
769 ULONG anodeoffset = 0;
770 BOOL eod = FALSE, found = FALSE;
772 /* Get directory and find object */
773 GetAnode(&anode, diranodenr, g);
774 while (!found && !eod)
776 dirblock = LoadDirBlock(anode.blocknr + anodeoffset, g);
777 if (dirblock)
779 de = FIRSTENTRY(dirblock);
780 while (de->next)
782 if (!(found = (de->anode == target)))
783 de = NEXTENTRY(de);
784 else
785 break;
788 if (!found)
789 eod = !NextBlock(&anode, &anodeoffset, g);
791 else
792 break;
795 if (!found)
796 return FALSE;
798 result->file.dirblock = dirblock;
799 result->file.direntry = de;
800 LOCK(dirblock);
801 return TRUE;
804 /**********************************************************************/
805 /* GET DIRECTORY CONTENTS */
806 /* GET DIRECTORY CONTENTS */
807 /* GET DIRECTORY CONTENTS */
808 /**********************************************************************/
811 /* ExamineFile
813 * Specification:
815 * - fill in fileinfoblock
816 * - prepares for ExamineNextFile
817 * - result: success
818 * *error: error if failure; else undefined
820 * - fib_DirEntryType must be equal to fib_EntryType
822 BOOL ExamineFile(listentry_t *file, struct FileInfoBlock * fib, SIPTR *error, globaldata * g)
824 struct volumedata *volume;
825 #if DELDIR
826 struct crootblockextension *rext;
827 union objectinfo *info;
828 #endif
830 /* volume must be or become currentvolume */
831 if (!file)
832 volume = g->currentvolume;
833 else
834 volume = file->volume;
836 /* fill in fib */
837 fib->fib_DiskKey = (IPTR)file; // I use it as dir-block-number for ExNext
838 if (!file || IsVolumeEntry(file))
840 fib->fib_DirEntryType = \
841 fib->fib_EntryType = ST_USERDIR;
842 fib->fib_Protection = (g->diskstate == ID_WRITE_PROTECTED) || g->softprotect;
843 fib->fib_Size = 0;
844 fib->fib_NumBlocks = 0;
846 if (volume->rblkextension)
848 fib->fib_Date.ds_Days = (ULONG)volume->rblkextension->blk.root_date[0];
849 fib->fib_Date.ds_Minute = (ULONG)volume->rblkextension->blk.root_date[1];
850 fib->fib_Date.ds_Tick = (ULONG)volume->rblkextension->blk.root_date[2];
852 else
854 fib->fib_Date.ds_Days = (ULONG)volume->rootblk->creationday;
855 fib->fib_Date.ds_Minute = (ULONG)volume->rootblk->creationminute;
856 fib->fib_Date.ds_Tick = (ULONG)volume->rootblk->creationtick;
859 CopyMem(volume->rootblk->diskname, fib->fib_FileName, volume->rootblk->diskname[0] + 1);
860 fib->fib_Comment[0] = 0x0;
861 //fib->fib_Reserved[0] = 0x0;
863 else
864 #if DELDIR
866 info = &file->info;
867 if (IsDelDir(*info))
869 rext = volume->rblkextension;
870 fib->fib_DiskKey = 0;
871 fib->fib_DirEntryType = \
872 fib->fib_EntryType = ST_USERDIR;
873 fib->fib_Protection = (ULONG)rext->blk.dd_protection;
874 fib->fib_Size = 0;
875 fib->fib_NumBlocks = 0;
876 fib->fib_Date.ds_Days = (ULONG)rext->blk.dd_creationday;
877 fib->fib_Date.ds_Minute = (ULONG)rext->blk.dd_creationminute;
878 fib->fib_Date.ds_Tick = (ULONG)rext->blk.dd_creationtick;
879 strcpy(fib->fib_FileName, deldirname);
880 fib->fib_Comment[0] = 0x0;
881 fib->fib_OwnerUID = (ULONG)rext->blk.dd_uid;
882 fib->fib_OwnerGID = (ULONG)rext->blk.dd_gid;
884 /* prepare ExNext() */
885 ((lockentry_t *)file)->nextanode = 0;
886 return DOSTRUE;
888 else if (IsDelFile(*info))
890 FillDelfileFib(NULL, info->delfile.slotnr, fib, g);
891 return DOSTRUE;
893 else
894 #endif
896 FillFib(file->info.file.direntry, fib, g);
898 #if DELDIR
900 #endif
902 /* prepare examinenext */
903 if (!file || file->type.flags.dir)
904 GetFirstEntry((lockentry_t *)file, g);
906 return DOSTRUE;
909 /* ExamineNextFile
911 * Specification:
913 * - used to obtain info on all directory entries
914 * - fill in fileinfoblock
916 * Quirks:
918 * - fib_DirEntryType must be equal to fib_EntryType
919 * - it is allowed to stop invoking before end of dir
920 * - for old compatibility: only DiskKey and FileName guaranteed to be the same
921 * on subsequent calls
922 * - it is possible to receive other packets between ExNext's -> special cases
923 * - the lock is passed is not always the same, but it is a lock to the same object
925 * Implementation:
927 * - fib_DiskKey contains address of lock; different->problems
929 * lock->nextentry contains pointer to be fetched entry. End of dir <=>
930 * lock->nextentry == {NULL, NULL}
933 BOOL ExamineNextFile(lockentry_t *file, struct FileInfoBlock * fib,
934 SIPTR *error, globaldata * g)
937 struct direntry *direntry;
939 *error = 0;
941 /* file must be lock on dir; NULL lock not allowed */
942 if (!file || !file->le.type.flags.dir)
944 *error = ERROR_OBJECT_WRONG_TYPE;
945 return DOSFALSE;
948 #if DELDIR
949 if (IsDelDir(file->le.info))
951 struct deldirentry *dde;
953 dde = GetDeldirEntry(&fib->fib_DiskKey, g);
954 if (dde)
956 FillDelfileFib(dde, fib->fib_DiskKey, fib, g);
957 fib->fib_DiskKey++;
958 return DOSTRUE;
960 else
962 *error = ERROR_NO_MORE_ENTRIES;
963 return DOSFALSE;
966 #endif
968 /* check if same lock used */
969 if ((IPTR)file != (IPTR)fib->fib_DiskKey)
971 DB(Trace(1, "ExamineNextFile", "Other lock!!\n"));
972 RemakeNextEntry(file, fib, g);
975 /* Check if end of dir */
976 if (file->nextentry.dirblock == NULL)
978 *error = ERROR_NO_MORE_ENTRIES;
979 return DOSFALSE;
982 /* Fill infoblock */
983 direntry = file->nextentry.direntry;
984 if (direntry->next)
986 fib->fib_DiskKey = (IPTR)file; // I use it as dir-block-number
987 FillFib(direntry, fib, g);
989 else
991 *error = ERROR_NO_MORE_ENTRIES;
992 ErrorMsg(AFS_ERROR_EX_NEXT_FAIL, NULL, g);
993 return DOSFALSE;
996 /* get next entry */
997 GetNextEntry(file, g);
998 return DOSTRUE;
1002 /* Fill fileinfoblock of a file
1004 static void FillFib(struct direntry *direntry, struct FileInfoBlock *fib, globaldata * g)
1006 struct extrafields extrafields;
1007 UBYTE *comment;
1008 FSIZE size;
1010 size = GetDEFileSize(direntry, g);
1011 /* fib->fib_DiskKey done by Examine(Next) */
1012 fib->fib_DirEntryType = direntry->type;
1013 fib->fib_EntryType = direntry->type;
1014 fib->fib_Protection = (ULONG)direntry->protection;
1015 fib->fib_Size = GetDEFileSize32(direntry, g);
1016 fib->fib_NumBlocks = (size / BLOCKSIZE) + (size % BLOCKSIZE > 0);
1017 fib->fib_Date.ds_Days = direntry->creationday;
1018 fib->fib_Date.ds_Minute = direntry->creationminute;
1019 fib->fib_Date.ds_Tick = direntry->creationtick;
1020 strncpy(fib->fib_FileName, (UBYTE *)&direntry->nlength, direntry->nlength + 1);
1021 fib->fib_FileName[direntry->nlength + 1] = 0;
1023 comment = (UBYTE *)&direntry->startofname + direntry->nlength;
1024 strncpy(fib->fib_Comment, comment, min(*comment + 1, CMSIZE));
1025 fib->fib_Comment[*comment + 1] = 0;
1027 if (g->dirextension)
1029 GetExtraFields(direntry, &extrafields);
1030 fib->fib_Protection |= extrafields.prot;
1031 fib->fib_OwnerUID = extrafields.uid;
1032 fib->fib_OwnerGID = extrafields.gid;
1034 #if ROLLOVER
1035 if (direntry->type == ST_ROLLOVERFILE)
1036 fib->fib_Size = extrafields.virtualsize;
1037 #endif
1041 /* Fill 'nextentry' of listentry [le]. If directory contains no entries,
1042 * nextentry will be set to NULL
1043 * > not for deldirs <
1045 static void GetFirstEntry(lockentry_t *le, globaldata * g)
1047 ULONG anodenr;
1049 if (!le)
1050 anodenr = ANODE_ROOTDIR;
1051 else
1052 anodenr = le->le.anodenr;
1054 le->nextanode = GetFirstNonEmptyDE(anodenr, &le->nextentry, g);
1057 /* Get first non empty direntry starting from anode [anodenr]
1058 * Returns {NULL, NULL} if end of dir
1060 static ULONG GetFirstNonEmptyDE(ULONG anodenr, struct fileinfo *info, globaldata *g)
1062 struct canode anode;
1063 ULONG nextsave;
1064 BOOL found = FALSE;
1066 anode.next = anodenr;
1067 while (!found)
1069 nextsave = anode.next;
1070 if (nextsave)
1072 GetAnode(&anode, anode.next, g);
1073 info->dirblock = LoadDirBlock(anode.blocknr, g);
1074 if (info->dirblock)
1075 info->direntry = FIRSTENTRY(info->dirblock);
1078 if (!nextsave || !info->dirblock)
1080 info->direntry = NULL;
1081 info->dirblock = NULL;
1082 found = TRUE;
1084 else if (info->direntry->next != 0)
1086 LOCK(info->dirblock);
1087 found = TRUE;
1091 return anode.nr;
1095 static void RemakeNextEntry(lockentry_t *file, struct FileInfoBlock *fib, globaldata *g)
1097 ULONG anodenr;
1098 UBYTE filename[FNSIZE];
1100 anodenr = file->le.anodenr;
1101 if (ddstricmp(fib->fib_FileName, file->le.volume->rootblk->diskname))
1102 GetFirstEntry(file, g);
1103 else
1105 BCPLtoCString(filename, fib->fib_FileName);
1106 if (!SearchInDir(anodenr, filename, (union objectinfo *)&file->nextentry, g))
1108 file->nextentry.dirblock = NULL;
1109 file->nextentry.direntry = NULL;
1110 file->nextanode = anodenr;
1112 else
1113 GetNextEntry(file, g);
1117 void GetNextEntry(lockentry_t *file, globaldata * g)
1119 struct canode anode;
1121 /* get nextentry */
1122 file->nextentry.direntry = NEXTENTRY(file->nextentry.direntry);
1124 /* no next entry? -> next block */
1125 if (!file->nextentry.direntry->next)
1127 /* NB: 'nextanode' is een verwarrende naam */
1128 GetAnode(&anode, file->nextanode, g);
1129 file->nextanode = GetFirstNonEmptyDE(anode.next, &file->nextentry, g);
1133 /* ExamineAll
1135 * fill buffer with directory information
1137 * Note: +- 2000 stack needed
1139 static ULONG FillInData(struct ExAllDataEXT *buffer, LONG type,
1140 struct direntry *direntry, ULONG spaceleft, globaldata *g)
1142 UWORD nameoffset, commentoffset = 0;
1143 UBYTE *name, *comment = NULL;
1144 UCOUNT size;
1145 struct extrafields extrafields;
1147 /* get location to put name */
1148 switch (type)
1150 case ED_NAME:
1151 size = offsetof(struct ExAllDataEXT, ed_Type);
1152 break;
1154 case ED_TYPE:
1155 size = offsetof(struct ExAllDataEXT, ed_Size);
1156 break;
1158 case ED_SIZE:
1159 size = offsetof(struct ExAllDataEXT, ed_Prot);
1160 break;
1162 case ED_PROTECTION:
1163 size = offsetof(struct ExAllDataEXT, ed_Days);
1164 break;
1166 case ED_DATE:
1167 size = offsetof(struct ExAllDataEXT, ed_Comment);
1168 break;
1170 case ED_COMMENT:
1171 size = offsetof(struct ExAllDataEXT, ed_OwnerUID);
1172 break;
1174 case ED_OWNER:
1175 #if EXTENDED_PACKETS_MORPHOS
1176 size = offsetof(struct ExAllDataEXT, ed_Size64);
1177 break;
1179 case ED_SIZE64:
1180 #endif
1181 size = sizeof(struct ExAllDataEXT);
1182 break;
1184 default:
1185 size = offsetof(struct ExAllDataEXT, ed_Type);
1186 break;
1189 /* size of name */
1190 nameoffset = size;
1191 name = &direntry->nlength;
1192 size += *name + 1;
1194 /* size of comment */
1195 if (type >= ED_COMMENT)
1197 commentoffset = size;
1198 comment = COMMENT(direntry);
1199 size += *comment + 1;
1202 /* check fit */
1203 size = (size + 1) & 0xfffe;
1204 if (size > spaceleft)
1205 return (0);
1207 /* get extra fields */
1208 GetExtraFields(direntry, &extrafields);
1210 /* copy */
1211 buffer->ed_Next = NULL;
1212 switch (type)
1214 #if EXTENDED_PACKETS_MORPHOS
1215 case ED_SIZE64:
1216 #if ROLLOVER
1217 if (direntry->type == ST_ROLLOVERFILE)
1218 buffer->ed_Size64 = extrafields.virtualsize;
1219 else
1220 #endif
1221 buffer->ed_Size64 = GetDEFileSize(direntry, g);
1222 #endif
1223 case ED_OWNER:
1224 buffer->ed_OwnerUID = extrafields.uid;
1225 buffer->ed_OwnerGID = extrafields.gid;
1227 case ED_COMMENT:
1228 buffer->ed_Comment = (UBYTE *)buffer + commentoffset;
1229 strncpy((UBYTE *)buffer + commentoffset, comment + 1, *comment);
1230 *((UBYTE *)buffer + commentoffset + *comment) = 0x0;
1232 case ED_DATE:
1233 buffer->ed_Days = direntry->creationday;
1234 buffer->ed_Mins = direntry->creationminute;
1235 buffer->ed_Ticks = direntry->creationtick;
1237 case ED_PROTECTION:
1238 buffer->ed_Prot = (ULONG)direntry->protection | extrafields.prot;
1240 case ED_SIZE:
1241 #if ROLLOVER
1242 if (direntry->type == ST_ROLLOVERFILE)
1243 buffer->ed_Size = extrafields.virtualsize;
1244 else
1245 #endif
1246 buffer->ed_Size = GetDEFileSize32(direntry, g);
1248 case ED_TYPE:
1249 buffer->ed_Type = direntry->type;
1251 case ED_NAME:
1253 strncpy((UBYTE *)buffer + nameoffset,
1254 (UBYTE *)&direntry->startofname, *name);
1255 *((UBYTE *)buffer + nameoffset + *name) = 0x0;
1257 default:
1258 buffer->ed_Name = (UBYTE *)buffer + nameoffset;
1261 return size;
1264 BOOL ExamineAll(lockentry_t *object, UBYTE *buffer, ULONG buflen,
1265 LONG type, struct ExAllControl * ctrl, SIPTR *error, globaldata * g)
1267 struct direntry *direntry;
1268 struct ExAllDataEXT *lasteaentry = NULL;
1269 ULONG spaceleft, size;
1270 UWORD j;
1271 BOOL eod;
1272 #if DELDIR
1273 struct deldirentry *dde;
1274 #endif
1276 ENTER("ExamineAll");
1278 /* init and check */
1279 if (!object || !object->le.type.flags.dir)
1281 *error = ERROR_OBJECT_WRONG_TYPE;
1282 return DOSFALSE;
1285 /* check type field */
1286 #if EXTENDED_PACKETS_MORPHOS
1287 if (type > ED_SIZE64 || type < ED_NAME)
1288 #else
1289 if (type > ED_OWNER || type < ED_NAME)
1290 #endif
1292 *error = ERROR_BAD_NUMBER;
1293 return DOSFALSE;
1296 /* Prepare loop */
1297 spaceleft = buflen;
1298 j = 0; // # direntries in buffer
1300 #if DELDIR
1301 if (IsDelDir(object->le.info))
1303 /* Main loop */
1304 while ((dde = GetDeldirEntry(&ctrl->eac_LastKey, g)))
1306 BOOL wanted;
1308 size = FillInDDEData((struct ExAllDataEXT *)buffer, type, dde, ctrl->eac_LastKey,
1309 spaceleft, g);
1310 if (size)
1312 /* check if entry is wanted (needs 1500 stack) */
1313 if (ctrl->eac_MatchFunc)
1314 wanted = CallHookPkt(ctrl->eac_MatchFunc, &type, buffer);
1315 else if (ctrl->eac_MatchString)
1316 wanted = MatchPatternNoCase(ctrl->eac_MatchString,
1317 ((struct ExAllDataEXT *)buffer)->ed_Name);
1318 else
1319 wanted = TRUE;
1321 /* if so update statistics */
1322 if (wanted)
1324 if (lasteaentry)
1325 lasteaentry->ed_Next = (struct ExAllDataEXT *)buffer;
1327 lasteaentry = (struct ExAllDataEXT *)buffer;
1328 buffer += size;
1329 spaceleft -= size;
1330 j++;
1333 ctrl->eac_LastKey++;
1335 else
1336 break; // doesn't fit
1340 /* Finish up */
1341 ctrl->eac_Entries = j;
1342 if (!dde)
1344 *error = ERROR_NO_MORE_ENTRIES;
1345 return DOSFALSE;
1347 else
1348 return DOSTRUE;
1350 #endif
1352 /* check if it's the first call and if same lock is used */
1353 if (ctrl->eac_LastKey == 0)
1354 GetFirstEntry(object, g);
1355 else if ((IPTR)object != (IPTR)ctrl->eac_LastKey)
1357 *error = ERROR_NO_MORE_ENTRIES;
1358 return DOSFALSE;
1361 DB(Trace(1, "ExamineAll", "matchfunc = %lx matchstring = %s\n", ctrl->eac_MatchFunc, ctrl->eac_MatchString));
1363 /* Main loop */
1364 while (1)
1366 BOOL wanted;
1368 if (!(eod = (object->nextentry.dirblock == NULL)))
1369 direntry = object->nextentry.direntry;
1370 else
1371 break;
1373 size = FillInData((struct ExAllDataEXT *)buffer, type, direntry,
1374 spaceleft, g);
1375 if (size)
1378 /* check if entry is wanted (needs 1500 stack) */
1379 if (ctrl->eac_MatchFunc)
1380 wanted = CallHookPkt(ctrl->eac_MatchFunc, &type, buffer);
1381 else if (ctrl->eac_MatchString)
1382 wanted = MatchPatternNoCase(ctrl->eac_MatchString,
1383 ((struct ExAllDataEXT *)buffer)->ed_Name);
1384 else
1385 wanted = TRUE;
1387 /* if so update statistics */
1388 if (wanted)
1390 if (lasteaentry)
1391 lasteaentry->ed_Next = (struct ExAllDataEXT *)buffer;
1393 lasteaentry = (struct ExAllDataEXT *)buffer;
1394 buffer += size;
1395 spaceleft -= size;
1396 j++;
1399 GetNextEntry(object, g);
1401 else
1402 break; // doesn't fit
1406 /* Finish up */
1407 ctrl->eac_LastKey = (IPTR)object;
1408 ctrl->eac_Entries = j;
1410 if (eod)
1412 *error = ERROR_NO_MORE_ENTRIES;
1413 return DOSFALSE;
1415 else
1416 return DOSTRUE;
1419 /**********************************************************************/
1420 /* ADD AND REMOVE FILES */
1421 /* ADD AND REMOVE FILES */
1422 /* ADD AND REMOVE FILES */
1423 /**********************************************************************/
1425 /* <NewFile>
1427 * NewFile creates a new file in a [directory] on currentvolume
1429 * input : - [directory]: directory of file;
1430 * - [filename]: name (without path) of file
1431 * - found: flag, file already present?. If so newfile == old
1433 * output: - [newfile]: fileinfo of new file (struct is managed by caller)
1434 * - [directory]: fileinfo of parent (can have changed if hardlink)
1436 * result: errornr; 0 = success
1438 * maxneeds: 1 nd + 2 na = 3 res
1440 * Note: 'directory' and 'newfile' may point to the same.
1442 ULONG NewFile (BOOL found, union objectinfo *directory, STRPTR filename, union objectinfo *newfile, globaldata *g)
1444 union objectinfo info;
1445 ULONG anodenr;
1446 SIPTR error;
1447 UBYTE entrybuffer[MAX_ENTRYSIZE];
1448 struct extrafields extrafields;
1449 struct direntry *destentry;
1450 struct canode anode;
1451 size_t l;
1452 #if VERSION23
1453 struct anodechain *achain;
1454 #endif
1456 DB(Trace(10, "NewFile", "%s\n", filename));
1457 /* check disk-writeprotection etc */
1458 if (!CheckVolume(g->currentvolume, 1, &error, g))
1459 return error;
1461 #if DELDIR
1462 if (IsDelDir(*directory))
1463 return ERROR_WRITE_PROTECTED;
1464 #endif
1466 /* check reserved area lock */
1467 if (ReservedAreaIsLocked)
1468 return ERROR_DISK_FULL;
1470 /* truncate filename to 31 characters */
1471 if (!(l = strlen(filename)))
1472 return ERROR_INVALID_COMPONENT_NAME;
1473 if (l > FILENAMESIZE - 1)
1474 filename[FILENAMESIZE - 1] = 0x0;
1476 if (found)
1479 * new version: take over direntry
1480 * (used to simply delete old and make new)
1482 info.file = newfile->file;
1483 anodenr = FIANODENR(&info.file);
1485 /* Check deleteprotection */
1486 if (IsVolume(info) || info.file.direntry->protection & FIBF_DELETE)
1487 return ERROR_DELETE_PROTECTED;
1489 /* If link, get real object. After this it has become a
1490 * ST_FILE
1492 if ((info.file.direntry->type == ST_LINKFILE) ||
1493 (info.file.direntry->type == ST_LINKDIR))
1495 struct canode linknode;
1497 GetExtraFields(info.file.direntry, &extrafields);
1498 anodenr = extrafields.link;
1499 GetAnode(&linknode, info.file.direntry->anode, g);
1500 if (!FetchObject(linknode.clustersize, anodenr, &info, g))
1501 return ERROR_OBJECT_NOT_FOUND;
1503 /* have to check protection again */
1504 if (info.file.direntry->protection & FIBF_DELETE)
1505 return ERROR_DELETE_PROTECTED;
1507 /* get parent */
1508 if (!GetParent(&info, directory, &error, g))
1509 return ERROR_OBJECT_NOT_FOUND;
1512 /* Check if there are outstanding locks on object */
1513 if (ScanLockList(HeadOf(&g->currentvolume->fileentries), anodenr))
1515 DB(Trace(1, "NewFile", "object in use"));
1516 return ERROR_OBJECT_IN_USE;
1519 if (!(achain = GetAnodeChain(anodenr, g)))
1520 return ERROR_NO_FREE_STORE;
1522 /* Free used space */
1523 if (g->deldirenabled && info.file.direntry->type == ST_FILE)
1525 int ddslot;
1527 /* free a slot to put old version in, inter. update possible */
1528 ddslot = AllocDeldirSlot(g);
1530 /* make replacement anode, because we want to reuse the old one */
1531 info.file.direntry->anode = achain->head.an.nr = AllocAnode(0, g);
1532 SaveAnode(&achain->head.an, achain->head.an.nr, g);
1533 AddToDeldir(&info, ddslot, g);
1534 info.file.direntry->anode = anodenr;
1537 /* Rollover files are essentially just 'reset' by overwriting:
1538 * only the virtualsize and offset are set to zero (extrafields)
1539 * Other files are deleted and recreated as a new file.
1541 if (info.file.direntry->type != ST_ROLLOVERFILE)
1543 /* Change directory entry */
1544 SetDEFileSize(info.file.direntry, 0, g);
1545 info.file.direntry->type = ST_FILE;
1546 MakeBlockDirty((struct cachedblock *)info.file.dirblock, g);
1548 /* Reclaim anode */
1549 anode.clustersize = 0;
1550 anode.blocknr = 0xffffffff;
1551 anode.next = 0;
1552 SaveAnode(&anode, anodenr, g);
1554 /* Delete old file (update possible) */
1555 if (g->deldirenabled && info.file.direntry->type == ST_FILE)
1556 FreeBlocksAC(achain, UINT_MAX, keepanodes, g);
1557 else
1558 FreeBlocksAC(achain, UINT_MAX, freeanodes, g);
1559 DetachAnodeChain(achain, g);
1562 /* Clear direntry extrafields */
1563 destentry = (struct direntry *)entrybuffer;
1564 memcpy(destentry, info.file.direntry, info.file.direntry->next);
1565 GetExtraFields(info.file.direntry, &extrafields);
1566 extrafields.virtualsize = extrafields.rollpointer = 0;
1567 AddExtraFields(destentry, &extrafields);
1568 ChangeDirEntry(info.file, destentry, directory, &info.file, g);
1569 newfile->file = info.file;
1570 return 0;
1573 /* direntry alloceren en invullen */
1574 if (MakeDirEntry(ST_FILE, filename, entrybuffer, g))
1576 if (AddDirectoryEntry(directory, (struct direntry *)entrybuffer, &newfile->file, g))
1577 return 0;
1578 else
1579 FreeAnode(((struct direntry *)entrybuffer)->anode, g);
1582 return ERROR_DISK_FULL;
1586 /* NewDir
1588 * Specification:
1590 * - make new dir
1591 * - returns fileentry (!) with exclusive lock
1593 * Implementation:
1595 * - check if file/dir exists
1596 * - make direntry
1597 * - make first dirblock
1599 * Similar to NewFile()
1601 * maxneeds: 2 nd, 3 na = 2 nablk : 4 res
1603 lockentry_t *NewDir(union objectinfo *parent, STRPTR dirname, SIPTR *error, globaldata * g)
1605 union objectinfo info;
1606 listentry_t *fileentry;
1607 listtype type;
1608 struct cdirblock *blk;
1609 ULONG parentnr, blocknr;
1610 UBYTE entrybuffer[MAX_ENTRYSIZE];
1611 size_t l;
1613 DB(Trace(1, "NewDir", "%s\n", dirname));
1615 /* check disk-writeprotection etc */
1616 if (!CheckVolume(g->currentvolume, 1, error, g))
1617 return NULL;
1619 #if DELDIR
1620 if (IsDelDir(*parent))
1622 *error = ERROR_WRITE_PROTECTED;
1623 return NULL;
1625 #endif
1627 /* check reserved area lock */
1628 if (ReservedAreaIsLocked)
1630 *error = ERROR_DISK_FULL;
1631 return NULL;
1634 /* checkvolume */
1635 if (IsVolume(*parent))
1636 parentnr = ANODE_ROOTDIR;
1637 else
1638 parentnr = parent->file.direntry->anode;
1640 /* truncate dirname to 31 characters */
1641 if (!(l = strlen(dirname)))
1643 *error = ERROR_INVALID_COMPONENT_NAME;
1644 return DOSFALSE;
1646 if (l > FILENAMESIZE - 1)
1647 dirname[FILENAMESIZE - 1] = 0x0;
1649 /* check if object exists */
1650 if (SearchInDir(parentnr, dirname, &info, g))
1652 *error = ERROR_OBJECT_EXISTS;
1653 return DOSFALSE;
1656 /* allocate directory entry, fill it. Make fileentry */
1657 if (!MakeDirEntry(ST_USERDIR, dirname, entrybuffer, g))
1658 goto error1;
1660 if (!AddDirectoryEntry(parent, (struct direntry *)entrybuffer, &info.file, g))
1662 FreeAnode(((struct direntry *)entrybuffer)->anode, g);
1663 error1:
1664 *error = ERROR_DISK_FULL;
1665 return DOSFALSE;
1668 type.value = ET_LOCK | ET_EXCLREAD;
1669 if (!(fileentry = MakeListEntry(&info, type, error, g)))
1670 goto error2;
1671 if (!AddListEntry(fileentry)) /* Should never fail, accessconflict impossible */
1673 ErrorMsg(AFS_ERROR_NEWDIR_ADDLISTENTRY, NULL, g);
1674 goto error2;
1677 /* Make first directoryblock (needed for parentfinding) */
1678 if (!(blocknr = AllocReservedBlock(g)))
1680 *error = ERROR_DISK_FULL;
1681 error2:
1682 FreeAnode(info.file.direntry->anode, g);
1683 RemoveDirEntry(info.file, g);
1684 if (fileentry)
1685 FreeListEntry(fileentry, g);
1686 DB(Trace(1, "Newdir", "disk full"));
1687 return DOSFALSE;
1690 blk = MakeDirBlock(blocknr, info.file.direntry->anode,
1691 info.file.direntry->anode, parentnr, g);
1692 (void)blk; // Unused
1694 return (lockentry_t *)fileentry;
1697 struct cdirblock *MakeDirBlock(ULONG blocknr, ULONG anodenr, ULONG rootanodenr,
1698 ULONG parentnr, globaldata * g)
1700 struct canode anode;
1701 struct cdirblock *blk;
1702 struct volumedata *volume = g->currentvolume;
1704 DB(Trace(10,"MakeDirBlock","blocknr = %lx\n", blocknr));
1706 /* fill in anode (allocated by MakeDirEntry) */
1707 anode.clustersize = 1;
1708 anode.blocknr = blocknr;
1709 anode.next = 0;
1710 SaveAnode(&anode, anodenr, g);
1712 blk = (struct cdirblock *)AllocLRU(g);
1713 blk->blk.id = DBLKID;
1714 blk->blk.anodenr = rootanodenr;
1715 blk->blk.parent = parentnr;
1716 blk->volume = volume;
1717 blk->blocknr = blocknr;
1718 blk->oldblocknr = 0;
1719 blk->changeflag = TRUE;
1721 Hash(blk, volume->dirblks, HASHM_DIR);
1722 LOCK(blk);
1723 return blk;
1727 /* DeleteObject
1729 * Specification:
1731 * - The object referenced by the info structure is removed
1732 * - The object must be on currentvolume
1734 * Implementation:
1736 * - check deleteprotection
1737 * - if dir, check if directory is empty
1738 * - check if there are outstanding locks on object
1739 * - remove object from directory and free anode
1740 * - rearrange & store directory
1742 * Don't check dirtycount!
1743 * info becomes INVALID!
1745 BOOL DeleteObject(union objectinfo * info, SIPTR *error, globaldata * g)
1747 ULONG anodenr;
1749 ENTER("DeleteObject");
1750 /* Check deleteprotection */
1751 #if DELDIR
1752 if (!info || info->deldir.special <= SPECIAL_DELFILE ||
1753 info->file.direntry->protection & FIBF_DELETE)
1754 #else
1755 if (!info || IsVolume(*info) || info->file.direntry->protection & FIBF_DELETE)
1756 #endif
1758 *error = ERROR_DELETE_PROTECTED;
1759 return DOSFALSE;
1762 /* Check if link, links can always be removed */
1763 if ((info->file.direntry->type == ST_LINKFILE) ||
1764 (info->file.direntry->type == ST_LINKDIR))
1766 return DeleteLink(&info->file, error, g);
1769 anodenr = FIANODENR(&info->file);
1771 /* Check if there are outstanding locks on object */
1772 if (ScanLockList(HeadOf(&g->currentvolume->fileentries), anodenr))
1774 DB(Trace(1, "Delete", "object in use"));
1775 *error = ERROR_OBJECT_IN_USE;
1776 return DOSFALSE;
1779 /* Check if object has links,
1780 * if it does the object should not be deleted,
1781 * just the direntry
1783 if (!RemapLinks(&info->file, g))
1785 /* Remove object from directory and free anode */
1786 if (info->file.direntry->type == ST_USERDIR)
1787 return DeleteDir(info, error, g);
1788 else
1790 /* ST_FILE or ST_SOFTLINK */
1791 struct anodechain *achain;
1792 int do_deldir = g->deldirenabled && info->file.direntry->type == ST_FILE;
1793 ULONG ddslot;
1795 if (!(achain = GetAnodeChain(anodenr, g)))
1797 *error = ERROR_NO_FREE_STORE;
1798 return DOSFALSE;
1800 if (do_deldir)
1802 ddslot = AllocDeldirSlot(g);
1803 AddToDeldir(info, ddslot, g);
1806 ChangeDirEntry(info->file, NULL, NULL, NULL, g); /* remove direntry */
1807 if (do_deldir)
1808 FreeBlocksAC(achain, UINT_MAX, keepanodes, g);
1809 else
1811 FreeBlocksAC(achain, UINT_MAX, freeanodes, g);
1812 FreeAnode(achain->head.an.nr, g);
1814 DetachAnodeChain(achain, g);
1818 return DOSTRUE;
1822 * Delete directory
1824 static BOOL DeleteDir(union objectinfo *info, SIPTR *error, globaldata * g)
1826 struct canode anode, chnode;
1827 struct volumedata *volume = g->currentvolume;
1828 struct cdirblock *dirblk;
1829 UWORD t;
1831 anode.nr = FIANODENR(&info->file);
1832 if (!DirIsEmpty(anode.nr, g))
1834 *error = ERROR_DIRECTORY_NOT_EMPTY;
1835 return DOSFALSE;
1837 else
1839 /* check if tobefreedcache is sufficiently large,
1840 * otherwise update disk
1842 chnode.next = anode.nr;
1843 for (t=1; chnode.next; t++)
1844 GetAnode(&chnode, chnode.next, g);
1846 if (2*t + alloc_data.rtbf_index > RTBF_THRESHOLD)
1847 UpdateDisk (g);
1849 /* do it (btw: fails if dirblock contains more than 128 empty
1850 * blocks)
1852 anode.next = anode.nr;
1853 while (anode.next)
1855 GetAnode(&anode, anode.next, g);
1857 /* remove dirblock from list if there */
1858 dirblk = (struct cdirblock *)CheckCache(volume->dirblks, HASHM_DIR, anode.blocknr, g);
1859 if (dirblk)
1861 MinRemove(dirblk);
1862 if (dirblk->changeflag)
1863 ResToBeFreed(dirblk->oldblocknr, g);
1865 FreeLRU((struct cachedblock *)dirblk);
1867 FreeAnode(anode.nr, g);
1868 ResToBeFreed(anode.blocknr , g);
1870 ChangeDirEntry(info->file, NULL, NULL, NULL, g); // delete entry from parentdir
1871 return DOSTRUE;
1876 * AFS Temporary extensions: kill empty, remove dir entry
1878 BOOL KillEmpty(union objectinfo * parent, globaldata * g)
1880 ULONG dirnodenr;
1881 SIPTR error;
1882 union objectinfo filefi;
1884 if (IsVolume(*parent))
1885 dirnodenr = ANODE_ROOTDIR;
1886 else
1887 dirnodenr = parent->file.direntry->anode;
1889 if (SearchInDir(dirnodenr, "", &filefi, g) && IsDir(filefi))
1890 return DeleteDir(&filefi, &error, g);
1891 else
1892 return DOSFALSE;
1896 * Remove a directory entry without freeing anything and without
1897 * checking validity
1899 LONG forced_RemoveDirEntry(union objectinfo *info, SIPTR *error, globaldata * g)
1901 if (!info || IsVolume(*info))
1903 *error = ERROR_DELETE_PROTECTED;
1904 return DOSFALSE;
1907 ChangeDirEntry(info->file, NULL, NULL, NULL, g); /* rearrange & store done by this function */
1908 return DOSTRUE;
1911 /* Check if directory with anodenr [anodenr] is empty
1912 * There can be multiple empty directory blocks
1914 static BOOL DirIsEmpty(ULONG anodenr, globaldata * g)
1916 struct canode anode;
1917 struct cdirblock *dirblok;
1919 GetAnode(&anode, anodenr, g);
1920 dirblok = LoadDirBlock(anode.blocknr, g);
1922 while (dirblok && (dirblok->blk.entries[0] == 0) && anode.next)
1924 GetAnode(&anode, anode.next, g);
1925 dirblok = LoadDirBlock(anode.blocknr, g);
1928 if (dirblok && ((dirblok->blk.entries[0]) == 0)) /* not empty->entries present */
1929 return DOSTRUE;
1930 else
1931 return DOSFALSE;
1934 #if DELDIR
1936 * Frees anodes without freeing blocks
1938 void FreeAnodesInChain(ULONG anodenr, globaldata * g)
1940 struct canode anode;
1941 struct crootblockextension *rext = g->currentvolume->rblkextension;
1943 DB(Trace(1, "FreeAnodeInChain", "anodenr: %ld \n", anodenr));
1944 GetAnode(&anode, anodenr, g);
1945 while (anode.nr) /* stops autom.: anode.nr of anode 0 == 0 */
1947 if (IsUpdateNeeded(RTBF_THRESHOLD))
1949 if (rext)
1951 rext->blk.tobedone.operation_id = PP_FREEANODECHAIN;
1952 rext->blk.tobedone.argument1 = anode.nr;
1953 rext->blk.tobedone.argument2 = 0;
1954 rext->blk.tobedone.argument3 = 0;
1957 UpdateDisk(g);
1960 FreeAnode(anode.nr, g);
1961 GetAnode(&anode, anode.next, g);
1964 if (rext)
1966 rext->blk.tobedone.operation_id = 0;
1967 rext->blk.tobedone.argument1 = 0;
1968 rext->blk.tobedone.argument2 = 0;
1969 rext->blk.tobedone.argument3 = 0;
1973 #endif /* DELDIR */
1975 /**********************************************************************/
1976 /* DIRECTORYOPERATIONS */
1977 /* DIRECTORYOPERATIONS */
1978 /* DIRECTORYOPERATIONS */
1979 /**********************************************************************/
1981 /* RenameAndMove
1983 * Specification:
1985 * - rename object
1986 * - renaming directories into a child not allowed!
1988 * Rename across devices tested in dd_Rename (DosToHandlerInterface)
1990 * Implementation:
1992 * - source ophalen
1993 * - check if new name allowed
1994 * - destination maken
1995 * - remove source direntry
1996 * - add destination direntry
1998 * maxneeds: 2 dblk changed, 1 new an : 3 res
2000 * sourcedi = objectinfo of source directory
2001 * destdi = objectinfo of destination directory
2002 * srcinfo = objectinfo of source
2003 * destinfo = objectinfo of destination
2004 * src- destanodenr = anodenr of source- destination directory
2006 BOOL RenameAndMove (union objectinfo *sourcedi, union objectinfo *srcinfo,
2007 union objectinfo *destdir, STRPTR destpath, SIPTR *error,
2008 globaldata * g)
2010 struct direntry *srcdirentry, *destentry;
2011 UBYTE entrybuffer[MAX_ENTRYSIZE];
2012 ULONG srcanodenr, destanodenr;
2013 WORD srcfieldoffset, destfieldoffset, fieldsize;
2014 union objectinfo destinfo, destdi;
2015 UBYTE *srccomment, *destcomment;
2016 STRPTR destname;
2018 /* fetch source info & path and check if exists */
2019 if (!(destname = GetFullPath (destdir, destpath, &destdi, error, g)))
2021 *error = ERROR_OBJECT_NOT_FOUND;
2022 return DOSFALSE;
2025 /* source nor destination may be a volume */
2026 if (IsVolume(*srcinfo) || !*destname)
2028 *error = ERROR_OBJECT_WRONG_TYPE;
2029 return DOSFALSE;
2032 #if DELDIR
2033 if (IsDelDir(*sourcedi) || IsDelDir(destdi) || IsDelDir(*srcinfo))
2035 *error = ERROR_WRITE_PROTECTED;
2036 return DOSFALSE;
2038 #endif
2040 /* check reserved area lock */
2041 if (ReservedAreaIsLocked)
2043 *error = ERROR_DISK_FULL;
2044 return DOSFALSE;
2047 srcdirentry = srcinfo->file.direntry;
2048 srccomment = COMMENT(srcdirentry);
2050 /* check if new name allowed
2051 * destpath should exist and file should not
2052 * %9.1 the same name IS allowed (rename 'hello' to 'Hello')
2054 if (FindObject(&destdi, destname, &destinfo, error, g))
2056 if (destinfo.file.direntry != srcinfo->file.direntry)
2058 *error = ERROR_OBJECT_EXISTS;
2059 return DOSFALSE;
2063 /* Test if a directory is being renamed to a child of itself. This is so
2064 * if:
2065 * 1) source (srcinfo) is a directory and
2066 * 2) sourcepath (sourcedi) <> destinationpath (destdi) and
2067 * 3) source (srcinfo) is part of destpath (destdi)
2068 * Example: rename a/b to a/b/c/d:
2069 * 1) a/b is dir [ok]; 2) a <> a/b/c [ok]; 3) a/b is part of a/b/c [ok]
2070 * Links need special attention!
2072 srcanodenr = IsRootA(*sourcedi) ? ANODE_ROOTDIR : FIANODENR(&sourcedi->file);
2073 destanodenr = IsRootA(destdi) ? ANODE_ROOTDIR : FIANODENR(&destdi.file);
2074 if (IsRealDir(*srcinfo) && (srcanodenr != destanodenr) &&
2075 IsChildOf(destdi, *srcinfo, g))
2077 *error = ERROR_OBJECT_IN_USE;
2078 return DOSFALSE;
2081 /* Make destination */
2082 destentry = (struct direntry *)&entrybuffer;
2084 /* copy header */
2085 memcpy(destentry, srcdirentry, offsetof(struct direntry, nlength));
2087 /* copy name */
2088 destentry->nlength = (UBYTE)strlen(destname);
2089 if (destentry->nlength > FILENAMESIZE - 1)
2090 destentry->nlength = FILENAMESIZE - 1;
2091 memcpy((UBYTE *)&destentry->startofname, destname, destentry->nlength);
2093 /* copy comment */
2094 destcomment = (UBYTE *)&destentry->startofname + destentry->nlength;
2095 memcpy(destcomment, srccomment, *srccomment + 1);
2097 /* copy fields */
2098 srcfieldoffset = (sizeof(struct direntry) + srcdirentry->nlength + *srccomment) & 0xfffe;
2099 destfieldoffset = (sizeof(struct direntry) + strlen(destname) + *srccomment) & 0xfffe;
2100 fieldsize = srcdirentry->next - srcfieldoffset;
2101 if (g->dirextension)
2102 memcpy((UBYTE *)destentry + destfieldoffset, (UBYTE *)srcdirentry + srcfieldoffset, fieldsize);
2104 /* set size */
2105 if (g->dirextension)
2106 destentry->next = (UBYTE)(destfieldoffset + fieldsize);
2107 else
2108 destentry->next = (UBYTE)destfieldoffset;
2110 /* remove source and add new direntry
2111 * Makes srcinfo INVALID
2113 PFSDoNotify(&srcinfo->file, TRUE, g);
2114 ChangeDirEntry(srcinfo->file, destentry, &destdi, &destinfo.file, g); // output:destinfo
2116 /* Update linklist and notify source if object moved across dirs
2118 if (destanodenr != srcanodenr)
2120 MoveLink (destentry, destanodenr, g);
2121 PFSDoNotify (&destinfo.file, TRUE, g);
2123 else
2125 PFSDoNotify (&destinfo.file, FALSE, g);
2128 /* If object is a directory and parent changed, update dirblocks */
2129 if (IsDir(destinfo) && (srcanodenr != destanodenr))
2131 struct canode anode;
2132 ULONG anodeoffset;
2133 BOOL gadoor = TRUE;
2135 anode.nr = destinfo.file.direntry->anode;
2136 anodeoffset = 0;
2137 GetAnode (&anode, anode.nr, g);
2138 for (anodeoffset = 0; gadoor; gadoor = NextBlock (&anode, &anodeoffset, g))
2140 struct cdirblock *blk;
2142 blk = LoadDirBlock (anode.blocknr + anodeoffset, g);
2143 if (blk)
2145 blk->blk.parent = destanodenr; // destination dir
2146 MakeBlockDirty ((struct cachedblock *)blk, g);
2151 return DOSTRUE;
2155 /* AddComment
2157 * - get old direntry
2158 * - make new direntry
2159 * - remove old direntry
2160 * - add new direntry
2162 * maxdirty: 1d, 1a = 2 res
2164 BOOL AddComment(union objectinfo * info, STRPTR comment, SIPTR *error, globaldata * g)
2166 struct direntry *sourceentry, *destentry;
2167 union objectinfo directory;
2168 UBYTE *destcomment, *srccomment, entrybuffer[MAX_ENTRYSIZE];
2169 WORD srcfieldoffset, destfieldoffset, fieldsize;
2171 DB(Trace(1, "AddComment", "%s\n", comment));
2172 #if DELDIR
2173 if (info->deldir.special <= SPECIAL_DELFILE)
2175 *error = ERROR_WRITE_PROTECTED;
2176 return DOSFALSE;
2178 #endif
2180 if (strlen(comment) > CMSIZE)
2182 *error = ERROR_COMMENT_TOO_BIG;
2183 return DOSFALSE;
2186 /* check reserved area lock */
2187 if (ReservedAreaIsLocked)
2189 *error = ERROR_DISK_FULL;
2190 return DOSFALSE;
2193 /* make new direntry */
2194 destentry = (struct direntry *)entrybuffer;
2195 sourceentry = info->file.direntry;
2197 /* copy header & name */
2198 memcpy(destentry, sourceentry, sizeof(struct direntry) + sourceentry->nlength - 1);
2200 /* copy comment */
2201 destcomment = COMMENT(destentry);
2202 *destcomment = strlen(comment);
2203 memcpy(destcomment + 1, comment, *destcomment);
2205 /* copy fields */
2206 srccomment = COMMENT(sourceentry);
2207 srcfieldoffset = (sizeof(struct direntry) + sourceentry->nlength + *srccomment) & 0xfffe;
2208 destfieldoffset = (sizeof(struct direntry) + sourceentry->nlength + *destcomment) & 0xfffe;
2209 fieldsize = sourceentry->next - srcfieldoffset;
2210 if (g->dirextension)
2211 memcpy((UBYTE *)destentry + destfieldoffset, (UBYTE *)sourceentry + srcfieldoffset, fieldsize);
2213 /* set size */
2214 if (g->dirextension)
2215 destentry->next = (UBYTE)(destfieldoffset + fieldsize);
2216 else
2217 destentry->next = (UBYTE)destfieldoffset;
2219 /* remove old directoryentry and add new */
2220 if (!GetParent(info, &directory, error, g))
2221 return DOSFALSE;
2222 else
2224 ChangeDirEntry(info->file, destentry, &directory, &info->file, g);
2225 return DOSTRUE;
2229 /* ProtectFile, SetDate
2231 * - simple direntry in cache change, no change in size
2232 * - CACHEDDIRENTRY must have changeflag
2234 * maxneeds: changes 1 block. NEVER allocates new block
2236 BOOL ProtectFile(struct fileinfo * file, ULONG protection, SIPTR *error, globaldata * g)
2238 ENTER("ProtectFile");
2240 // isvolume check already done in dostohandler..
2242 // if (!file || !file->direntry) /* @XLV */
2243 // {
2244 // *error = ERROR_OBJECT_WRONG_TYPE;
2245 // return DOSFALSE;
2246 // }
2248 #if DELDIR
2249 if ((*(union objectinfo *)file).delfile.special <= SPECIAL_DELFILE)
2251 if ((*(union objectinfo *)file).delfile.special == SPECIAL_DELDIR)
2253 protection &= DELENTRY_PROT_AND_MASK;
2254 protection |= DELENTRY_PROT_OR_MASK;
2255 g->currentvolume->rblkextension->blk.dd_protection = protection;
2256 MakeBlockDirty((struct cachedblock *)g->currentvolume->rblkextension, g);
2257 return DOSTRUE;
2260 *error = ERROR_WRITE_PROTECTED;
2261 return DOSFALSE;
2263 #endif
2265 /* check reserved area lock */
2266 if (ReservedAreaIsLocked)
2268 *error = ERROR_DISK_FULL;
2269 return DOSFALSE;
2272 file->direntry->protection = protection;
2274 /* add second part of protection */
2275 if (g->dirextension)
2277 union objectinfo directory;
2278 struct direntry *sourceentry, *destentry;
2279 struct extrafields extrafields;
2280 UBYTE entrybuffer[MAX_ENTRYSIZE];
2282 /* make new direntry */
2283 destentry = (struct direntry *)entrybuffer;
2284 sourceentry = file->direntry;
2286 /* copy source */
2287 memcpy(destentry, sourceentry, sourceentry->next);
2289 /* set new extrafields */
2290 GetExtraFields(sourceentry, &extrafields);
2291 extrafields.prot = protection;
2292 AddExtraFields(destentry, &extrafields);
2294 /* commit changes */
2295 if (!GetParent((union objectinfo *)file, &directory, error, g))
2296 return DOSFALSE;
2297 else
2298 ChangeDirEntry(*file, destentry, &directory, file, g);
2301 /* mark block for update and return success */
2302 MakeBlockDirty((struct cachedblock *)file->dirblock, g);
2303 return DOSTRUE;
2306 BOOL SetOwnerID(struct fileinfo * file, ULONG owner, SIPTR *error, globaldata * g)
2308 struct extrafields extrafields;
2309 union objectinfo directory;
2310 struct direntry *sourceentry, *destentry;
2311 UBYTE entrybuffer[MAX_ENTRYSIZE];
2313 ENTER("SetOwnerID");
2314 #if DELDIR
2315 if ((*(union objectinfo *)file).delfile.special <= SPECIAL_DELFILE)
2317 if ((*(union objectinfo *)file).delfile.special == SPECIAL_DELDIR)
2319 struct crootblockextension *rext = g->currentvolume->rblkextension;
2321 rext->blk.dd_uid = owner >> 16;
2322 rext->blk.dd_gid = owner & 0xffff;
2323 MakeBlockDirty ((struct cachedblock *)rext, g);
2324 return DOSTRUE;
2327 *error = ERROR_WRITE_PROTECTED;
2328 return DOSFALSE;
2330 #endif
2332 if (!g->dirextension)
2334 *error = ERROR_ACTION_NOT_KNOWN;
2335 return DOSFALSE;
2338 /* check reserved area lock */
2339 if (ReservedAreaIsLocked)
2341 *error = ERROR_DISK_FULL;
2342 return DOSFALSE;
2345 /* make new direntry */
2346 destentry = (struct direntry *)entrybuffer;
2347 sourceentry = file->direntry;
2349 /* copy source */
2350 memcpy(destentry, sourceentry, sourceentry->next);
2352 /* set new extrafields */
2353 GetExtraFields(sourceentry, &extrafields);
2354 extrafields.uid = owner >> 16;
2355 extrafields.gid = owner & 0xffff;
2356 AddExtraFields(destentry, &extrafields);
2358 /* commit changes */
2359 if (!GetParent((union objectinfo *)file, &directory, error, g))
2360 return DOSFALSE;
2361 else
2362 ChangeDirEntry(*file, destentry, &directory, file, g);
2364 MakeBlockDirty((struct cachedblock *)file->dirblock, g);
2365 return DOSTRUE;
2368 LONG ReadSoftLink(union objectinfo *linkfi, char *buffer, ULONG size, SIPTR *error, globaldata * g)
2370 struct canode anode;
2371 UBYTE softblock[1024];
2373 ENTER("ReadSoftLink");
2374 if (!linkfi || IsVolume(*linkfi))
2376 *error = ERROR_OBJECT_WRONG_TYPE;
2377 return -1;
2380 if (linkfi->file.direntry->fsize + (g->unparsed ? strlen(g->unparsed) : 0) > size)
2381 return -2;
2383 GetAnode(&anode, linkfi->file.direntry->anode, g);
2384 DiskRead(softblock, 1, anode.blocknr, g);
2385 strcpy(buffer, softblock);
2387 if (g->unparsed)
2388 strcat(buffer, g->unparsed);
2390 return (LONG)strlen(buffer);
2393 BOOL CreateSoftLink(union objectinfo *linkdir, STRPTR linkname, STRPTR softlink,
2394 union objectinfo *newlink, SIPTR *error, globaldata * g)
2396 ULONG anodenr;
2397 UBYTE entrybuffer[MAX_ENTRYSIZE];
2398 struct direntry *de;
2399 UBYTE softblock[1024];
2400 struct canode anode;
2401 size_t l;
2403 ENTER("CreateSoftLink");
2404 #if DELDIR
2405 if (IsDelDir(*linkdir))
2407 *error = ERROR_WRITE_PROTECTED;
2408 return DOSFALSE;
2410 #endif
2412 /* check disk-writeprotection etc */
2413 if (!CheckVolume(g->currentvolume, 1, error, g))
2414 return DOSFALSE;
2416 /* get anodenr of directory */
2417 if (!linkdir || IsVolume(*linkdir))
2418 anodenr = ANODE_ROOTDIR;
2419 else
2421 anodenr = FIANODENR(&linkdir->file);
2422 LOCK(linkdir->file.dirblock);
2425 /* truncate filename to 31 characters */
2426 if (!(l = strlen(linkname)))
2428 *error = ERROR_INVALID_COMPONENT_NAME;
2429 return DOSFALSE;
2431 if (l > FILENAMESIZE - 1)
2432 linkname[FILENAMESIZE - 1] = 0x0;
2434 /* check reserved area lock */
2435 if (ReservedAreaIsLocked)
2437 *error = ERROR_DISK_FULL;
2438 return DOSFALSE;
2441 /* check if a file by that name already exists */
2442 if (SearchInDir(anodenr, linkname, newlink, g))
2444 *error = ERROR_OBJECT_EXISTS;
2445 return DOSFALSE;
2448 /* make directory entry
2449 * the anode allocated is the link list element
2451 de = (struct direntry *)entrybuffer;
2452 if (!MakeDirEntry(ST_SOFTLINK, linkname, entrybuffer, g))
2453 return DOSFALSE;
2455 /* store directoryentry */
2456 if (!AddDirectoryEntry(linkdir, (struct direntry *)entrybuffer, &newlink->file, g))
2457 goto error1;
2459 /* fill directory entry */
2460 if (!(AllocateBlocks(de->anode, 1, g)))
2462 *error = ERROR_DISK_FULL;
2463 goto error2;
2466 GetAnode(&anode, de->anode, g);
2467 memset(softblock, 0, 1024);
2468 strcpy(softblock, softlink);
2469 DiskWrite(softblock, 1, anode.blocknr, g);
2470 newlink->file.direntry->fsize = strlen(softblock);
2471 return DOSTRUE;
2473 /* errors */
2474 error2:
2475 RemoveDirEntry(newlink->file, g);
2476 error1:
2477 FreeAnode(de->anode, g);
2478 return DOSFALSE;
2482 * linkdir: directory (in)
2483 * linkname: name (in)
2484 * object: object to link to (in) (dir must be locked)
2485 * newlink: result (out)
2487 BOOL CreateLink(union objectinfo * linkdir, STRPTR linkname, union objectinfo * object,
2488 union objectinfo * newlink, SIPTR *error, globaldata * g)
2490 union objectinfo info, odi;
2491 ULONG anodenr, linklist;
2492 struct direntry *objectentry, *destentry;
2493 UBYTE entrybuffer[MAX_ENTRYSIZE];
2494 struct extrafields extrafields;
2495 struct canode linknode;
2496 size_t l;
2498 ENTER("CreateLink");
2499 #if DELDIR
2500 if (IsDelDir(*linkdir) || IsDelDir(*object) || IsDelFile(*object))
2502 *error = ERROR_WRITE_PROTECTED;
2503 return DOSFALSE;
2505 #endif
2507 /* check if operation possible */
2508 if (!g->dirextension)
2510 *error = ERROR_ACTION_NOT_KNOWN;
2511 return DOSFALSE;
2514 /* check disk-writeprotection etc */
2515 if (!CheckVolume(g->currentvolume, 1, error, g))
2516 return DOSFALSE;
2518 /* get anodenr */
2519 if (!linkdir || IsVolume(*linkdir))
2521 anodenr = ANODE_ROOTDIR;
2523 else
2525 anodenr = FIANODENR(&linkdir->file);
2526 LOCK(linkdir->file.dirblock);
2529 /* truncate filename to 31 characters */
2530 if (!(l = strlen(linkname)))
2532 *error = ERROR_INVALID_COMPONENT_NAME;
2533 return DOSFALSE;
2535 if (l > FILENAMESIZE - 1)
2536 linkname[FILENAMESIZE - 1] = 0x0;
2538 /* check reserved area lock */
2539 if (ReservedAreaIsLocked)
2541 *error = ERROR_DISK_FULL;
2542 return DOSFALSE;
2545 /* check if a file by that name already exists */
2546 if (SearchInDir(anodenr, linkname, &info, g))
2548 *error = ERROR_OBJECT_EXISTS;
2549 return DOSFALSE;
2552 /* make directory entry
2553 * the anode allocated is the link list element
2555 if (!MakeDirEntry(ST_LINKDIR, linkname, entrybuffer, g))
2556 return DOSFALSE;
2558 /* add link info */
2559 destentry = (struct direntry *)entrybuffer;
2560 objectentry = object->file.direntry;
2561 #if MULTIUSER
2562 GetExtraFields(destentry, &extrafields);
2563 #else
2564 memset(&extrafields, 0, sizeof(struct extrafields));
2565 #endif
2566 extrafields.link = objectentry->anode;
2567 AddExtraFields(destentry, &extrafields);
2569 /* copy object info */
2570 destentry->fsize = objectentry->fsize;
2571 switch (objectentry->type)
2573 case ST_FILE:
2574 case ST_SOFTLINK:
2575 case ST_ROLLOVERFILE:
2576 case ST_LINKFILE:
2577 destentry->type = ST_LINKFILE;
2578 break;
2580 default:
2581 destentry->type = ST_LINKDIR;
2582 break;
2585 /* store directoryentry */
2586 if (!AddDirectoryEntry(linkdir, destentry, &newlink->file, g))
2588 FreeAnode(destentry->anode, g);
2589 return DOSFALSE;
2592 /* make linknode */
2593 linknode.clustersize = object->file.dirblock->blk.anodenr;
2594 linknode.blocknr = newlink->file.dirblock->blk.anodenr;
2595 linknode.next = 0;
2596 SaveAnode(&linknode, newlink->file.direntry->anode, g);
2598 /* change objectentry */
2599 GetExtraFields(objectentry, &extrafields);
2600 if (!(linklist = extrafields.link))
2602 /* there were no links yet ->
2603 * add link field. Use entrybuffer and destentry pointer
2605 extrafields.link = linklist = newlink->file.direntry->anode;
2606 memcpy(entrybuffer, objectentry, objectentry->next);
2607 AddExtraFields(destentry, &extrafields);
2609 if (!GetParent(object, &odi, error, g))
2610 return DOSFALSE; /* serious! should not happen */
2612 ChangeDirEntry(object->file, destentry, &odi, &object->file, g);
2614 else
2616 /* add new link to chain */
2617 GetAnode(&linknode, extrafields.link, g);
2618 while (linknode.next)
2619 GetAnode(&linknode, linknode.next, g);
2620 linknode.next = newlink->file.direntry->anode;
2621 SaveAnode(&linknode, linknode.nr, g);
2624 return DOSTRUE;
2627 BOOL SetDate(union objectinfo *file, struct DateStamp *date, SIPTR *error, globaldata *g)
2629 ENTER("SetDate");
2631 #if DELDIR
2632 if (file->deldir.special <= SPECIAL_DELFILE)
2634 *error = ERROR_WRITE_PROTECTED;
2635 return DOSFALSE;
2637 #endif
2639 if (!CheckVolume(file->file.dirblock->volume, 1, error, g))
2640 return DOSFALSE;
2642 file->file.direntry->creationday = (UWORD)date->ds_Days;
2643 file->file.direntry->creationminute = (UWORD)date->ds_Minute;
2644 file->file.direntry->creationtick = (UWORD)date->ds_Tick;
2645 MakeBlockDirty((struct cachedblock *)file->file.dirblock, g);
2646 return DOSTRUE;
2649 void Touch(struct fileinfo *info, globaldata * g) // ook archiveflag..
2651 struct DateStamp time;
2653 DateStamp(&time);
2655 #if VERSION23
2656 if (IsVolume(*((union objectinfo *)info)) &&
2657 g->currentvolume->rblkextension)
2659 g->currentvolume->rblkextension->blk.root_date[0] = (UWORD)time.ds_Days;
2660 g->currentvolume->rblkextension->blk.root_date[1] = (UWORD)time.ds_Minute;
2661 g->currentvolume->rblkextension->blk.root_date[2] = (UWORD)time.ds_Tick;
2662 MakeBlockDirty((struct cachedblock *)g->currentvolume->rblkextension, g);
2664 else
2665 #endif
2667 if (!IsVolume(*((union objectinfo *)info)))
2669 info->direntry->creationday = (UWORD)time.ds_Days;
2670 info->direntry->creationminute = (UWORD)time.ds_Minute;
2671 info->direntry->creationtick = (UWORD)time.ds_Tick;
2672 info->direntry->protection &= ~FIBF_ARCHIVE; // clear archivebit (eor)
2674 MakeBlockDirty((struct cachedblock *)info->dirblock, g);
2679 #if ROLLOVER
2681 * dir: directory (in)
2682 * rollname: name of rollover file (in)
2683 * result: created rolloverfile (out)
2685 BOOL CreateRollover(union objectinfo *dir, STRPTR rollname, ULONG size,
2686 union objectinfo *result, SIPTR *error, globaldata * g)
2688 ULONG anodenr;
2689 struct direntry *de;
2690 UBYTE entrybuffer[MAX_ENTRYSIZE];
2691 struct anodechain *ac;
2692 size_t l;
2694 DB(Trace(1, "CreateRollover", "name %s size %d \n", rollname, size));
2695 #if DELDIR
2696 /* no write access to deldir */
2697 if (IsDelDir(*dir))
2699 *error = ERROR_WRITE_PROTECTED;
2700 return DOSFALSE;
2702 #endif
2704 /* check if operation possible */
2705 if (!g->dirextension)
2707 *error = ERROR_ACTION_NOT_KNOWN;
2708 return DOSFALSE;
2711 /* check size */
2712 if (!size)
2714 *error = ERROR_BAD_NUMBER;
2715 return DOSFALSE;
2718 /* check disk-writeprotection etc */
2719 if (!CheckVolume(g->currentvolume, 1, error, g))
2720 return DOSFALSE;
2722 /* get anodenr of directory */
2723 if (!dir || IsVolume(*dir))
2724 anodenr = ANODE_ROOTDIR;
2725 else
2727 anodenr = FIANODENR(&dir->file);
2728 LOCK(dir->file.dirblock);
2731 /* truncate filename to 31 characters */
2732 if (!(l = strlen(rollname)))
2734 *error = ERROR_INVALID_COMPONENT_NAME;
2735 return DOSFALSE;
2737 if (l > FILENAMESIZE - 1)
2738 rollname[FILENAMESIZE - 1] = 0x0;
2740 /* check reserved area lock */
2741 if (ReservedAreaIsLocked)
2743 *error = ERROR_DISK_FULL;
2744 return DOSFALSE;
2747 /* check if a file by that name already exists */
2748 if (SearchInDir(anodenr, rollname, result, g))
2750 *error = ERROR_OBJECT_EXISTS;
2751 return DOSFALSE;
2754 /* make directory entry
2755 * the anode allocated is the link list element
2757 de = (struct direntry *)entrybuffer;
2758 if (!MakeDirEntry(ST_ROLLOVERFILE, rollname, entrybuffer, g))
2759 return DOSFALSE;
2761 /* add rollover info is not necessary, since
2762 * both virtualsize and rollpointer are initially
2763 * zero
2765 if (!(ac = GetAnodeChain(de->anode, g)))
2766 goto error1;
2768 /* store directoryentry */
2769 if (!AddDirectoryEntry(dir, de, &result->file, g))
2770 goto error2;
2772 /* allocate blocks
2773 * in case of an intermediate update, the rollover file will
2774 * be temporarily committed smaller
2776 if (!(AllocateBlocksAC(ac, size, &result->file, g)))
2777 goto error3;
2779 /* set real size
2781 result->file.direntry->fsize = size << BLOCKSHIFT;
2782 return DOSTRUE;
2784 /* errors */
2785 error3:
2786 RemoveDirEntry(result->file, g);
2787 error2:
2788 DetachAnodeChain(ac, g);
2789 error1:
2790 FreeAnode(de->anode, g);
2791 return DOSFALSE;
2794 /* changes/reads rollover information of rollfile using roinfo. Objectinfo of
2795 * 'rollfile' is updated, if so needed. Roinfo is always filled with the current
2796 * rollfile settings (AFTER update). The real filesize is only changed when the
2797 * realsize fiels is unequal zero.
2799 ULONG SetRollover(fileentry_t *rollfile, struct rolloverinfo *roinfo, globaldata *g)
2801 union objectinfo directory;
2802 struct extrafields extrafields;
2803 struct direntry *sourcede, *destde;
2804 UBYTE entrybuffer[MAX_ENTRYSIZE];
2805 SIPTR error;
2806 ULONG realsize;
2808 /* check if file is rollover file */
2809 if (!IsRollover(rollfile->le.info))
2810 return ERROR_OBJECT_WRONG_TYPE;
2812 destde = (struct direntry *)entrybuffer;
2813 sourcede = rollfile->le.info.file.direntry;
2814 GetExtraFields(sourcede, &extrafields);
2815 if (roinfo->set)
2817 if (roinfo->realsize)
2819 realsize = roinfo->realsize & ~(BLOCKSIZE-1);
2820 if (!realsize) realsize = BLOCKSIZE;
2822 else
2823 realsize = sourcede->fsize;
2825 /* virtualsize and rollpointer can not extend past end
2826 * of file. virtualsize has to be <= realsize - 1
2828 if (roinfo->rollpointer > realsize ||
2829 roinfo->virtualsize >= realsize)
2830 return ERROR_SEEK_ERROR;
2832 if (roinfo->realsize)
2833 ChangeFileSize(rollfile, realsize, OFFSET_BEGINNING, &error, g);
2834 roinfo->realsize = sourcede->fsize;
2835 memcpy(destde, sourcede, sourcede->next);
2836 extrafields.virtualsize = roinfo->virtualsize;
2837 extrafields.rollpointer = roinfo->rollpointer;
2838 AddExtraFields(destde, &extrafields);
2840 if (!GetParent(&rollfile->le.info, &directory, &error, g))
2841 return error;
2842 else
2843 ChangeDirEntry(rollfile->le.info.file, destde, &directory,
2844 &rollfile->le.info.file, g);
2846 else
2848 roinfo->realsize = sourcede->fsize;
2849 roinfo->virtualsize = extrafields.virtualsize;
2850 roinfo->rollpointer = extrafields.rollpointer;
2853 return 0;
2856 #endif /* ROLLOVER */
2858 /**********************************************************************/
2859 /* LL CHANGE DIRENTRY */
2860 /* LL CHANGE DIRENTRY */
2861 /* LL CHANGE DIRENTRY */
2862 /**********************************************************************/
2865 /* Change a directoryentry. Covers all reference changing too
2867 * If direntry==NULL no new direntry is to be added, only removed.
2868 * result may be NULL then as well
2870 * in: from, to, destdir
2871 * out: result
2873 * from can become INVALID..
2876 void ChangeDirEntry(struct fileinfo from, struct direntry *to,
2877 union objectinfo *destdir, struct fileinfo *result, globaldata * g)
2879 ULONG destanodenr = IsRoot(destdir) ? ANODE_ROOTDIR : FIANODENR(&destdir->file);
2881 /* check whether a 'within dir' rename */
2882 if (to && destanodenr == from.dirblock->blk.anodenr)
2883 RenameWithinDir(from, to, result, g);
2884 else
2885 RenameAcrossDirs(from, to, destdir, result, g);
2889 * Move a file from one dir to another
2890 * NULL = delete allowed
2892 static void RenameAcrossDirs(struct fileinfo from, struct direntry *to,
2893 union objectinfo *destdir, struct fileinfo *result, globaldata * g)
2895 UWORD removedlen;
2897 /* remove old entry (invalidates 'destdir') */
2898 removedlen = from.direntry->next;
2899 RemoveDirEntry(from, g);
2900 if (to)
2902 /* test on volume is not necessary, because file.dirblock = volume.volume !=
2903 * from.dirblock
2904 * restore 'destdir' (can be invalidated by RemoveDirEntry)
2906 if (destdir->file.dirblock == from.dirblock &&
2907 destdir->file.direntry > from.direntry)
2909 destdir->file.direntry = (struct direntry *)
2910 ((UBYTE *)destdir->file.direntry - removedlen);
2913 /* add new entry */
2914 AddDirectoryEntry(destdir, to, result, g);
2917 UpdateChangedRef(from, result, -removedlen, g);
2918 if (result)
2919 LOCK(result->dirblock);
2923 * Rename file within dir
2924 * NULL destination not allowed
2926 static void RenameWithinDir(struct fileinfo from, struct direntry *to,
2927 struct fileinfo *result, globaldata * g)
2929 int spaceneeded;
2930 struct fileinfo mover;
2932 LOCK(from.dirblock);
2933 mover.direntry = FIRSTENTRY(from.dirblock);
2934 mover.dirblock = from.dirblock;
2935 spaceneeded = to->next - from.direntry->next;
2936 if (spaceneeded <= 0)
2937 RenameInPlace(from, to, result, g);
2938 else
2940 /* make space in block
2942 while (!CheckFit(from.dirblock, spaceneeded, g) && from.direntry != mover.direntry)
2944 from.direntry = (struct direntry *)((UBYTE *)from.direntry - mover.direntry->next);
2945 MoveToPrevious(mover, mover.direntry, result, g);
2948 if (CheckFit(from.dirblock, spaceneeded, g))
2949 RenameInPlace(from, to, result, g);
2950 else
2951 MoveToPrevious(from, to, result, g);
2954 LOCK(result->dirblock);
2960 * Check if direntry will fit in,
2961 * returns position to place it if ok
2963 static struct direntry *CheckFit(struct cdirblock *blok, int needed, globaldata * g)
2965 struct direntry *entry;
2966 int i;
2968 /* goto end of dirblock */
2969 entry = FIRSTENTRY(blok);
2970 for (i = 0; entry->next; entry = NEXTENTRY(entry))
2971 i += entry->next;
2973 if (needed + i + 1 <= DB_ENTRYSPACE)
2974 return entry;
2975 else
2976 return NULL;
2980 * Moves firstentry to previous block, changing it to to. To can point to de.direntry
2981 * if wanted.
2982 * Return new fileinfo in 'result'
2983 * NB: no need to touch parent: MTP is always followed by another function
2984 * on the block.
2986 static BOOL MoveToPrevious(struct fileinfo de, struct direntry *to, struct fileinfo *result, globaldata * g)
2988 struct direntry *dest;
2989 struct cdirblock *prevblock;
2990 struct canode anode;
2991 int removedlen;
2992 ULONG prev;
2994 LOCK(de.dirblock);
2996 /* get previous block */
2997 GetAnode(&anode, de.dirblock->blk.anodenr, g);
2998 prev = 0;
2999 while (anode.blocknr != de.dirblock->blocknr && anode.next)
3001 prev = anode.nr;
3002 GetAnode(&anode, anode.next, g);
3005 /* savety check */
3006 if (anode.blocknr != de.dirblock->blocknr)
3008 ErrorMsg(AFS_ERROR_CACHE_INCONSISTENCY, NULL, g);
3009 return FALSE;
3012 /* Get dirblock in question
3013 * Special case : previous == 0!!->add new head!!
3015 if (prev)
3017 GetAnode(&anode, prev, g);
3018 if (!(prevblock = LoadDirBlock(anode.blocknr, g)))
3019 return FALSE;
3022 /* Add new entry */
3023 if (prev && (dest = CheckFit(prevblock, to->next, g)))
3025 memcpy(dest, to, to->next);
3026 *(UBYTE *)NEXTENTRY(dest) = 0; /* end of dirblock */
3027 result->direntry = dest;
3028 result->dirblock = prevblock;
3030 else
3032 /* make new dirblock .. */
3033 ULONG parent;
3034 struct canode newanode;
3035 struct cdirblock *newblock;
3037 newanode.clustersize = 1;
3038 parent = de.dirblock->blk.parent;
3039 if (!(newanode.blocknr = AllocReservedBlock(g)))
3040 return FALSE;
3042 if (!prev)
3044 GetAnode(&anode, de.dirblock->blk.anodenr, g);
3045 newanode.nr = anode.nr;
3046 newanode.next = anode.nr = AllocAnode (anode.next ? anode.next : anode.nr, g);
3048 else
3050 newanode.nr = AllocAnode (anode.nr, g);
3051 newanode.next = anode.next;
3052 anode.next = newanode.nr;
3055 SaveAnode(&anode, anode.nr, g);
3056 newblock = MakeDirBlock(newanode.blocknr, newanode.nr, de.dirblock->blk.anodenr, parent, g);
3057 SaveAnode(&newanode, newanode.nr, g); /* MUST be done AFTER MakeDirBlock */
3059 /* add entry */
3060 dest = FIRSTENTRY(newblock);
3061 memcpy(dest, to, to->next);
3062 *(UBYTE *)NEXTENTRY(dest) = 0; /* end of dirblock */
3063 result->direntry = dest;
3064 result->dirblock = newblock;
3067 LOCK(result->dirblock);
3068 MakeBlockDirty((struct cachedblock *)result->dirblock, g);
3070 /* remove old entry & make blocks dirty */
3071 removedlen = de.direntry->next;
3072 RemoveDirEntry(de, g);
3074 /* update references */
3075 UpdateChangedRef(de, result, -removedlen, g);
3076 return TRUE;
3080 * There HAS to be sufficient space!!
3082 static void RenameInPlace(struct fileinfo from, struct direntry *to, struct fileinfo *result, globaldata * g)
3084 UBYTE *dest, *start, *end;
3085 SIPTR error;
3086 ULONG movelen;
3087 int diff;
3088 union objectinfo parent;
3090 LOCK(from.dirblock);
3092 /* change date parent */
3093 if (GetParent((union objectinfo *)&from, &parent, &error, g))
3094 Touch(&parent.file, g);
3096 /* make place for new entry */
3097 diff = to->next - from.direntry->next;
3098 dest = (UBYTE *)from.direntry + to->next;
3099 start = (UBYTE *)from.direntry + from.direntry->next;
3100 end = (UBYTE *)&(from.dirblock->blk) + g->rootblock->reserved_blksize;
3101 movelen = (diff > 0) ? (end - dest) : (end - start);
3102 memmove(dest, start, movelen);
3104 /* fill in new entry */
3105 memcpy((UBYTE *)from.direntry, to, to->next);
3107 /* fill in result and make block dirty */
3108 *result = from;
3109 MakeBlockDirty((struct cachedblock *)from.dirblock, g);
3111 /* update references */
3112 UpdateChangedRef(from, result, diff, g);
3115 /* RemoveDirEntry
3117 * Simply shift the directryentry out with memmove(dest, src, len)
3118 * References are not corrected (see changedirentry)
3120 * makes all fileinfo's in same block invalid !!
3122 static void RemoveDirEntry(struct fileinfo info, globaldata * g)
3124 UBYTE *endofblok, *startofblok, *destofblok, *startofclear;
3125 UWORD clearlen;
3126 SIPTR error;
3127 union objectinfo parent;
3129 LOCK(info.dirblock);
3131 /* change date parent %6.5 */
3132 if (GetParent((union objectinfo *)&info, &parent, &error, g))
3133 Touch(&parent.file, g);
3135 /* remove direntry */
3136 destofblok = (UBYTE *)info.direntry;
3137 startofblok = destofblok + info.direntry->next;
3138 endofblok = (UBYTE *)&(info.dirblock->blk) + g->rootblock->reserved_blksize;
3139 startofclear = endofblok - info.direntry->next;
3140 clearlen = info.direntry->next;
3141 memmove(destofblok, startofblok, endofblok - startofblok);
3143 /* makes info invalid!! */
3144 if (info.direntry->next)
3145 memset(startofclear, 0, clearlen);
3146 MakeBlockDirty((struct cachedblock *)info.dirblock, g); // %6.2
3151 /* AddDirectoryEntry
3153 * Add a directoryentry to a directory.
3154 * Tries to add the directoryentry at the end of an existing directoryblock. If
3155 * that fails, create a new one.
3157 * Operates on currentvolume
3159 * input : - dir: directory to add directoryentry too
3160 * - newentry: the new directoryentry
3162 * output: - newinfo: pointer to direntry and directoryblock the entry
3163 * was added to
3165 * NB: A) there should ALWAYS be at least one dirblock
3166 * B) assumes CURRENTVOLUME
3168 static BOOL AddDirectoryEntry(union objectinfo *dir, struct direntry *newentry,
3169 struct fileinfo *newinfo, globaldata * g)
3171 struct canode anode;
3172 ULONG anodeoffset = 0, diranodenr;
3173 struct cdirblock *blok;
3174 struct direntry *entry = NULL;
3175 BOOL done = FALSE, eof = FALSE;
3176 UCOUNT i;
3178 if (!dir || IsVolume(*dir))
3179 diranodenr = ANODE_ROOTDIR;
3180 else
3181 diranodenr = dir->file.direntry->anode;
3183 /* check if space in existing dirblocks */
3184 for (GetAnode(&anode, diranodenr, g); !done && !eof; eof = !NextBlock(&anode, &anodeoffset, g))
3186 if (!(blok = LoadDirBlock(anode.blocknr + anodeoffset, g)))
3187 break;
3189 entry = (struct direntry *)&blok->blk.entries;
3191 /* goto end of dirblock; i = aantal gebruikte bytes */
3192 for (i = 0; entry->next; entry = NEXTENTRY(entry))
3193 i += entry->next;
3195 /* does it fit in this block? (keep space for trailing 0) */
3196 if (i + newentry->next + 1 < DB_ENTRYSPACE)
3198 memcpy(entry, newentry, newentry->next);
3199 *(UBYTE *)NEXTENTRY(entry) = 0; // dirblock afsluiten
3201 done = TRUE;
3202 break;
3206 /* no->new dirblock (eof <=> anode is end of chain)
3207 * We will make the new dirblock at the >start< of
3208 * the chain.
3209 * We always allocate new anode
3211 if (!done && eof)
3213 ULONG parent;
3214 struct canode newanode;
3216 newanode.clustersize = 1;
3217 parent = blok->blk.parent;
3218 if (!(newanode.blocknr = AllocReservedBlock(g)))
3219 return FALSE;
3220 GetAnode (&anode, diranodenr, g);
3221 newanode.nr = diranodenr;
3222 newanode.next = anode.nr = AllocAnode (anode.next ? anode.next : anode.nr, g);
3223 SaveAnode(&anode, anode.nr, g);
3224 blok = MakeDirBlock (newanode.blocknr, newanode.nr, diranodenr, parent, g);
3225 SaveAnode (&newanode, newanode.nr, g);
3226 entry = (struct direntry *)&blok->blk.entries;
3227 memcpy(entry, newentry, newentry->next);
3228 *(UBYTE *)NEXTENTRY(entry) = 0; // mark end of dirblock
3232 /* fill newinfo */
3233 newinfo->direntry = entry;
3234 newinfo->dirblock = blok;
3236 /* update notify */
3237 PFSUpdateNotify(blok->blk.anodenr, &entry->nlength, entry->anode, g);
3238 if (blok)
3240 LOCK(blok);
3241 MakeBlockDirty((struct cachedblock *)blok, g);
3244 Touch(&dir->file, g);
3245 return TRUE;
3250 * Update references
3251 * diff is direntry size difference (new - original)
3253 static void UpdateChangedRef(struct fileinfo from, struct fileinfo *to, int diff, globaldata * g)
3255 struct volumedata *volume = from.dirblock->volume;
3256 listentry_t *fe;
3258 for (fe = HeadOf(&volume->fileentries); fe->next; fe = fe->next)
3260 /* only dirs and files can be in a directory, but the volume *
3261 * of volumeinfos can never point to a cached block, so a
3262 * type != ETF_VOLUME check is not necessary. Just check the
3263 * dirblock pointer
3265 if (fe->info.file.dirblock == from.dirblock)
3267 /* is het de targetentry ? */
3268 if (fe->info.file.direntry == from.direntry)
3270 if (to)
3271 fe->info.file = *to;
3273 else
3275 /* take only entries after target */
3276 if (fe->info.file.direntry > from.direntry)
3278 fe->info.file.direntry = (struct direntry *)
3279 ((UBYTE *)fe->info.file.direntry + diff);
3284 /* check for exnext references */
3285 if (fe->type.flags.dir)
3287 lockentry_t *dle = (lockentry_t *)fe;
3289 if (dle->nextentry.dirblock == from.dirblock)
3291 if ((dle->nextentry.direntry == from.direntry)
3292 && (!dle->nextentry.direntry->next))
3294 GetNextEntry(dle, g);
3296 else
3298 /* take only entries after target */
3299 if (dle->nextentry.direntry > from.direntry)
3301 dle->nextentry.direntry = (struct direntry *)
3302 ((UBYTE *)dle->nextentry.direntry + diff);
3311 /* MakeDirEntry
3313 * Used by L2.NewFile, L2.NewDir
3315 * Make a new directoryentry. The filename is not checked. Allocates anode
3316 * for file/dir
3318 * input :
3319 * - type: ST_FILE, ST_DIR etc ..
3320 * - name: objectname
3321 * - entrybuffer: place to put direntry (char buffer of size MAX_ENTRYSIZE)
3323 * output: - info: objectinfo of new directoryentry
3325 static BOOL MakeDirEntry(BYTE type, UBYTE *name, UBYTE *entrybuffer, globaldata * g)
3327 UWORD entrysize;
3328 struct direntry *direntry;
3329 struct DateStamp time;
3330 MUFS(struct extrafields extrafields);
3332 entrysize = ((sizeof(struct direntry) + strlen(name)) & 0xfffe);
3333 if (g->dirextension)
3334 entrysize += 2;
3335 direntry = (struct direntry *)entrybuffer;
3336 memset(direntry, 0, entrysize);
3338 #if MULTIUSER
3339 if (g->muFS_ready)
3341 extrafields.link = 0;
3342 extrafields.uid = g->user->uid;
3343 extrafields.gid = g->user->gid;
3344 extrafields.prot = muGetDefProtection(g->action->dp_Port->mp_SigTask);
3345 direntry->protection = extrafields.prot;
3346 extrafields.prot &= 0xffffff00;
3348 #endif
3350 DateStamp(&time);
3351 if (!(direntry->anode = AllocAnode(0, g)))
3352 return FALSE;
3354 direntry->next = entrysize;
3355 direntry->type = type;
3356 // direntry->fsize = 0;
3357 direntry->creationday = (UWORD)time.ds_Days;
3358 direntry->creationminute = (UWORD)time.ds_Minute;
3359 direntry->creationtick = (UWORD)time.ds_Tick;
3360 // direntry->protection = 0x00; // RWED
3361 direntry->nlength = strlen(name);
3363 // the trailing 0 of strcpy() creates the empty comment!
3364 // the flags field following this is 0 by the memset call
3365 strcpy((UBYTE *)&direntry->startofname, name);
3367 #if MULTIUSER
3368 if (g->dirextension && g->muFS_ready)
3369 AddExtraFields(direntry, &extrafields);
3370 #endif
3372 return TRUE;
3375 /**********************************************************************/
3376 /* LOWLEVEL */
3377 /* LOWLEVEL */
3378 /* LOWLEVEL */
3379 /**********************************************************************/
3381 /* NULL => failure
3382 * The loaded dirblock is locked immediately (prevents flushing)
3385 struct cdirblock *LoadDirBlock(ULONG blocknr, globaldata * g)
3387 struct cdirblock *dirblk;
3388 struct volumedata *volume = g->currentvolume;
3390 DB(Trace(1, "LoadDirBlock", "loading block %lx\n", blocknr));
3391 // -I- check if already in cache
3392 if (!(dirblk = (struct cdirblock *)CheckCache(volume->dirblks, HASHM_DIR, blocknr, g)))
3394 // -II- not in cache -> put it in
3395 dirblk = (struct cdirblock *)AllocLRU(g);
3397 DB(Trace(10, "LoadDirBlock", "loading block %lx from disk\n", blocknr));
3398 if (RawRead((UBYTE *)&dirblk->blk, RESCLUSTER, blocknr, g) == 0)
3400 if (dirblk->blk.id == DBLKID)
3402 dirblk->volume = g->currentvolume;
3403 dirblk->blocknr = blocknr;
3404 dirblk->used = FALSE;
3405 dirblk->changeflag = FALSE;
3406 Hash(dirblk, volume->dirblks, HASHM_DIR);
3407 UpdateReference(blocknr, dirblk, g); // %10
3409 else
3411 ULONG args[2];
3412 args[0] = dirblk->blk.id;
3413 args[1] = blocknr;
3414 FreeLRU((struct cachedblock *)dirblk);
3415 ErrorMsg(AFS_ERROR_DNV_WRONG_DIRID, args, g);
3416 return NULL;
3419 else
3421 FreeLRU((struct cachedblock *)dirblk);
3422 DB(Trace(5, "LoadDirBlock", "loading block %lx failed\n", blocknr));
3423 // ErrorMsg(AFS_ERROR_DNV_LOAD_DIRBLOCK, NULL, g); // #$%^&??
3424 // DebugOn;DebugMsgNum("blocknr", blocknr);
3425 return NULL;
3429 EXIT("LoadDirBlock");
3430 return dirblk;
3433 static BOOL IsChildOf(union objectinfo child, union objectinfo parent, globaldata * g)
3435 SIPTR error;
3436 union objectinfo up;
3437 BOOL goon = TRUE;
3439 while (goon && !IsSameOI(child, parent))
3441 goon = GetParent(&child, &up, &error, g);
3442 child = up;
3445 if (IsSameOI(child, parent))
3446 return DOSTRUE;
3447 else
3448 return DOSFALSE;
3451 FSIZE GetDEFileSize(struct direntry *direntry, globaldata *g)
3453 if (!LARGE_FILE_SIZE || !g->largefile)
3454 return direntry->fsize;
3455 #if LARGE_FILE_SIZE
3456 else {
3457 struct extrafields extrafields;
3458 GetExtraFields(direntry, &extrafields);
3459 return direntry->fsize | ((FSIZE)extrafields.fsizex << 32);
3461 #endif
3464 ULONG GetDEFileSize32(struct direntry *direntry, globaldata *g)
3466 FSIZE size;
3468 if (!LARGE_FILE_SIZE || !g->largefile) {
3469 return direntry->fsize;
3470 } else {
3471 size = GetDEFileSize(direntry, g);
3472 if (size > MAXFILESIZE32)
3473 size = MAXFILESIZE32;
3474 return (ULONG)size;
3478 void SetDEFileSize(struct direntry *direntry, FSIZE size, globaldata *g)
3480 if (!LARGE_FILE_SIZE || !g->largefile)
3481 direntry->fsize = (ULONG)size;
3482 #if LARGE_FILE_SIZE
3483 else {
3484 struct extrafields extrafields;
3485 UWORD high = (UWORD)(size >> 32);
3486 GetExtraFields(direntry, &extrafields);
3487 if (extrafields.fsizex != high) {
3488 extrafields.fsizex = high;
3489 AddExtraFields(direntry, &extrafields);
3491 direntry->fsize = (ULONG)size;
3493 #endif
3496 FSIZE GetDDFileSize(struct deldirentry *dde, globaldata *g)
3498 if (!LARGE_FILE_SIZE || !g->largefile || dde->filename[0] > DELENTRYFNSIZE)
3499 return dde->fsize;
3500 #if LARGE_FILE_SIZE
3501 else
3502 return dde->fsize | ((FSIZE)dde->fsizex << 32);
3503 #endif
3506 ULONG GetDDFileSize32(struct deldirentry *dde, globaldata *g)
3508 FSIZE size = GetDDFileSize(dde, g);
3509 if (size > MAXFILESIZE32)
3510 size = MAXFILESIZE32;
3511 return (ULONG)size;
3514 void SetDDFileSize(struct deldirentry *dde, FSIZE size, globaldata *g)
3516 dde->fsize = (ULONG)size;
3517 #if LARGE_FILE_SIZE
3518 if (!LARGE_FILE_SIZE || !g->largefile)
3519 return;
3520 dde->fsizex = (UWORD)(size >> 32);
3521 #endif
3525 * GetExtraFields (normal file only)
3527 void GetExtraFields(struct direntry *direntry, struct extrafields *extrafields)
3529 UWORD *extra = (UWORD *)extrafields;
3530 UWORD *fields = (UWORD *)(((UBYTE *)direntry) + direntry->next);
3531 UWORD flags, i;
3533 flags = *(--fields);
3534 for (i = 0; i < sizeof(struct extrafields) / 2; i++, flags >>= 1)
3535 *(extra++) = (flags & 1) ? *(--fields) : 0;
3537 /* patch protection lower 8 bits */
3538 extrafields->prot |= direntry->protection;
3541 #if DELDIR
3542 #if MULTIUSER
3543 static void GetExtraFieldsDD(struct extrafields *extrafields, globaldata * g);
3544 static void GetExtraFieldsRoot(struct extrafields *extrafields, globaldata * g);
3545 void GetExtraFieldsOI(union objectinfo *info, struct extrafields *extrafields, globaldata * g)
3547 if (IsVolume(*info))
3548 GetExtraFieldsRoot(extrafields, g);
3549 else if (IsDelDir(*info) || IsDelFile(*info))
3550 GetExtraFieldsDD(extrafields, g);
3551 else
3552 GetExtraFields(info->file.direntry, extrafields);
3555 static void GetExtraFieldsDD(struct extrafields *extrafields, globaldata * g)
3557 struct crootblockextension *rext = g->currentvolume->rblkextension;
3559 extrafields->link = 0;
3560 extrafields->uid = rext->blk.dd_uid;
3561 extrafields->gid = rext->blk.dd_gid;
3562 extrafields->prot = rext->blk.dd_protection;
3565 static void GetExtraFieldsRoot(struct extrafields *extrafields, globaldata * g)
3567 memset(extrafields, 0, sizeof(struct extrafields));
3569 #endif
3570 #endif
3572 void AddExtraFields(struct direntry *direntry, struct extrafields *extra)
3574 UWORD offset, *dirext;
3575 UWORD array[16], i = 0, j = 0;
3576 UWORD flags = 0, orvalue;
3577 UWORD *fields = (UWORD *)extra;
3579 /* patch protection lower 8 bits */
3580 extra->prot &= 0xffffff00;
3581 offset = (sizeof(struct direntry) + (direntry->nlength) + *COMMENT(direntry)) & 0xfffe;
3582 dirext = (UWORD *)((UBYTE *)(direntry) + (UBYTE)offset);
3584 orvalue = 1;
3585 /* fill packed field array */
3586 for (i = 0; i < sizeof(struct extrafields) / 2; i++)
3588 if (*fields)
3590 array[j++] = *fields++;
3591 flags |= orvalue;
3593 else
3595 fields++;
3598 orvalue <<= 1;
3601 /* add fields to direntry */
3602 i = j;
3603 while (i)
3604 *dirext++ = array[--i];
3605 *dirext++ = flags;
3607 direntry->next = offset + 2 * j + 2;
3612 * Updates size field of links
3614 void UpdateLinks(struct direntry *object, globaldata * g)
3616 struct canode linklist;
3617 union objectinfo loi;
3618 struct extrafields extrafields;
3619 ULONG linknr;
3621 ENTER("UpdateLinks");
3622 GetExtraFields(object, &extrafields);
3623 linknr = extrafields.link;
3624 while (linknr)
3626 /* Update link: get link object info and update size */
3627 GetAnode(&linklist, linknr, g);
3628 FetchObject(linklist.blocknr, linklist.nr, &loi, g);
3629 loi.file.direntry->fsize = object->fsize;
3630 MakeBlockDirty((struct cachedblock *)loi.file.dirblock, g);
3631 linknr = linklist.next;
3636 * Removes link from linklist and kills direntry
3638 static BOOL DeleteLink(struct fileinfo *link, SIPTR *error, globaldata * g)
3640 struct canode linknode, linklist;
3641 struct extrafields extrafields;
3642 union objectinfo object, directory;
3643 UBYTE entrybuffer[MAX_ENTRYSIZE];
3645 /* get node to remove */
3646 GetAnode(&linknode, link->direntry->anode, g);
3647 GetExtraFields(link->direntry, &extrafields);
3649 /* delete old entry */
3650 ChangeDirEntry(*link, NULL, NULL, NULL, g);
3652 /* get object */
3653 FetchObject(linknode.clustersize, extrafields.link, &object, g);
3654 GetExtraFields(object.file.direntry, &extrafields);
3656 /* if the object lists our link as the first link, redirect it to the next one */
3657 if (extrafields.link == linknode.nr)
3659 extrafields.link = linknode.next;
3660 memcpy(entrybuffer, object.file.direntry, object.file.direntry->next);
3661 AddExtraFields((struct direntry *)entrybuffer, &extrafields);
3662 if (!GetParent(&object, &directory, error, g)) {
3663 *error = ERROR_DISK_NOT_VALIDATED;
3664 return DOSFALSE; // should never happen
3666 else {
3667 ChangeDirEntry(object.file, (struct direntry *)entrybuffer, &directory, &object.file, g);
3670 /* otherwise simply remove the link from the list of links */
3671 else
3673 GetAnode(&linklist, extrafields.link, g);
3674 while (linklist.next != linknode.nr)
3675 GetAnode(&linklist, linklist.next, g);
3677 linklist.next = linknode.next;
3678 SaveAnode(&linklist, linklist.nr, g);
3681 FreeAnode(linknode.nr, g);
3682 return DOSTRUE;
3686 * Removes head of linklist and promotes first link as
3687 * master (NB: object is the main object, NOT a link).
3688 * Returns linkstate: TRUE: a link was promoted
3689 * FALSE: there was no link to promote
3691 static BOOL RemapLinks(struct fileinfo *object, globaldata * g)
3693 struct extrafields extrafields;
3694 struct canode linknode;
3695 union objectinfo link, directory;
3696 struct direntry *destentry;
3697 UBYTE entrybuffer[MAX_ENTRYSIZE];
3698 SIPTR error;
3700 ENTER("RemapLinks");
3701 /* get head of linklist */
3702 GetExtraFields(object->direntry, &extrafields);
3703 if (extrafields.link == 0)
3704 return FALSE;
3706 /* the file has links; get head of list
3707 * we are going to promote this link to
3708 * an object
3710 GetAnode(&linknode, extrafields.link, g);
3712 /* get direntry belonging to this linknode */
3713 FetchObject(linknode.blocknr, linknode.nr, &link, g);
3715 /* Promote it from link to object */
3716 destentry = (struct direntry *)entrybuffer;
3717 memcpy(destentry, link.file.direntry, link.file.direntry->next);
3718 GetExtraFields(link.file.direntry, &extrafields);
3719 destentry->type = object->direntry->type; // is this necessary?
3720 destentry->fsize = object->direntry->fsize; // is this necessary?
3721 destentry->anode = object->direntry->anode; // is this necessary?
3723 extrafields.link = linknode.next;
3724 AddExtraFields(destentry, &extrafields);
3726 /* Free old linklist node */
3727 FreeAnode(linknode.nr, g);
3729 /* Remove source direntry */
3730 ChangeDirEntry(*object, NULL, NULL, NULL, g);
3732 /* Refetch new head (can have become invalid) */
3733 FetchObject(linknode.blocknr, linknode.nr, &link, g);
3734 if (GetParent(&link, &directory, &error, g))
3735 ChangeDirEntry(link.file, destentry, &directory, &link.file, g);
3737 /* object directory has changed; update link chain
3738 * new directory is the old chain head was in: linknode.linkdir (== linknode.blocknr)
3740 UpdateLinkDir(link.file.direntry, linknode.blocknr, g);
3741 return TRUE;
3745 * Update linklist to reflect new directory of linked to object
3747 static void UpdateLinkDir(struct direntry *object, ULONG newdiran, globaldata * g)
3749 struct canode linklist;
3750 struct extrafields extrafields;
3751 ULONG linknr;
3753 ENTER("UpdateLinkDir");
3754 GetExtraFields(object, &extrafields);
3755 linknr = extrafields.link;
3756 while (linknr)
3758 /* update linklist: change clustersize (== object dir) */
3759 GetAnode(&linklist, linknr, g);
3760 linklist.clustersize = newdiran;
3761 SaveAnode(&linklist, linklist.nr, g);
3762 linknr = linklist.next;
3767 * Update linklist to reflect moved node
3768 * (is supercopy of UpdateLinkDir)
3770 static void MoveLink(struct direntry *object, ULONG newdiran, globaldata *g)
3772 struct canode linklist;
3773 struct extrafields extrafields;
3774 ULONG linknr;
3776 ENTER("MoveLink");
3777 GetExtraFields(object, &extrafields);
3779 /* check if is link or linked to */
3780 if (!(linknr = extrafields.link))
3782 return;
3785 /* check filetype */
3786 if (object->type == ST_LINKDIR || object->type == ST_LINKFILE)
3788 /* it is a link -> just change the linkdir */
3789 GetAnode(&linklist, object->anode, g);
3790 linklist.blocknr = newdiran;
3791 SaveAnode(&linklist, linklist.nr, g);
3793 else
3795 /* it is the head (linked to) */
3796 while (linknr)
3798 /* update linklist: change clustersize (== object dir) */
3799 GetAnode(&linklist, linknr, g);
3800 linklist.clustersize = newdiran; /* the object's directory */
3801 SaveAnode(&linklist, linklist.nr, g);
3802 linknr = linklist.next;
3808 #if DELDIR
3810 /**********************************************************************/
3811 /* DELDIR */
3812 /**********************************************************************/
3814 /* SearchInDeldir
3816 * Search an object in the del-directory and return the objectinfo if found
3818 * input : - delname: name of object to be searched for
3819 * output: - result: the searched for object
3820 * result: deldirentry * or NULL
3822 static struct deldirentry *SearchInDeldir(STRPTR delname, union objectinfo *result, globaldata * g)
3824 struct deldirentry *dde;
3825 struct cdeldirblock *dblk;
3826 UBYTE *delnumptr;
3827 UBYTE intl_name[PATHSIZE];
3828 unsigned slotnr, offset;
3830 ENTER("SearchInDeldir");
3831 if (!(delnumptr = strrchr(delname, DELENTRY_SEP)))
3832 return FALSE; /* no delentry seperator */
3833 stcd_i(delnumptr + 1, (int *) &slotnr); /* retrieve the slotnr */
3835 *delnumptr = 0; /* patch string to get filename part */
3836 ctodstr(delname, intl_name);
3838 /* truncate to maximum length */
3839 if (intl_name[0] > FILENAMESIZE - 1)
3840 intl_name[0] = FILENAMESIZE - 1;
3842 intltoupper(intl_name); /* international uppercase objectname */
3843 *delnumptr = DELENTRY_SEP;
3845 /* 4.3: get deldir block */
3846 if (!(dblk = GetDeldirBlock(slotnr/DELENTRIES_PER_BLOCK, g)))
3847 return FALSE;
3849 offset = slotnr % DELENTRIES_PER_BLOCK;
3851 dde = &dblk->blk.entries[offset];
3852 if (intlcmp(intl_name, dde->filename))
3854 if (!IsDelfileValid(dde, dblk, g))
3855 return NULL;
3857 result->delfile.special = SPECIAL_DELFILE;
3858 result->delfile.slotnr = slotnr;
3859 LOCK(dblk);
3860 return dde;
3863 return NULL;
3867 * Test if delfile is valid by scanning it's blocks
3869 static BOOL IsDelfileValid(struct deldirentry *dde, struct cdeldirblock *ddblk, globaldata * g)
3871 struct canode anode;
3873 /* check if deldirentry actually used */
3874 if (!dde->anodenr)
3875 return FALSE;
3877 /* scan all blocks in the anodelist for validness */
3878 for (anode.nr = dde->anodenr; anode.nr; anode.nr = anode.next)
3880 GetAnode(&anode, anode.nr, g);
3881 if (BlockTaken(&anode, g))
3883 /* free attached anodechain */
3884 FreeAnodesInChain(dde->anodenr, g); /* only FREE anodes, not blocks!! */
3885 dde->anodenr = 0;
3886 MakeBlockDirty((struct cachedblock *)ddblk, g);
3887 return FALSE;
3891 return TRUE;
3895 * Check if the blocks referenced by an anode are taken
3897 static BOOL BlockTaken(struct canode *anode, globaldata * g)
3899 ULONG size, bmoffset, bmseqnr, field, i, j, blocknr;
3900 struct cbitmapblock *bitmap;
3902 i = (anode->blocknr - alloc_data.bitmapstart) / 32; // longwordnr
3903 size = (anode->clustersize + 31) / 32;
3904 bmseqnr = i / alloc_data.longsperbmb;
3905 bmoffset = i % alloc_data.longsperbmb;
3907 while (size)
3909 /* get first bitmapblock */
3910 bitmap = GetBitmapBlock(bmseqnr, g);
3912 /* check all blocks */
3913 while (bmoffset < alloc_data.longsperbmb)
3915 /* check all bits in field */
3916 field = bitmap->blk.bitmap[bmoffset];
3917 for (i = 0, j = 1 << 31; i < 32; j >>= 1, i++)
3919 if (!(field & j))
3921 /* block is taken, check it out */
3922 blocknr = (bmseqnr * alloc_data.longsperbmb + bmoffset) * 32 + i +
3923 alloc_data.bitmapstart;
3924 if (blocknr >= anode->blocknr && blocknr < anode->blocknr + anode->clustersize)
3925 return TRUE;
3928 bmoffset++;
3929 if (!--size)
3930 break;
3933 /* get ready for next block */
3934 bmseqnr = (bmseqnr + 1) % (alloc_data.no_bmb);
3935 bmoffset = 0;
3938 return FALSE;
3942 * fill in fib. fib->fib_DiskKey must be the deldirentry number
3944 static void FillDelfileFib(struct deldirentry *dde, ULONG slotnr, struct FileInfoBlock *fib, globaldata *g)
3946 struct crootblockextension *rext;
3947 UBYTE appbuffer[6];
3948 UBYTE *nameptr;
3949 unsigned i;
3950 FSIZE size;
3952 if (!dde)
3953 if (!(dde = GetDeldirEntryQuick(slotnr, g)))
3954 dde = GetDeldirEntryQuick(0, g);
3956 rext = g->currentvolume->rblkextension;
3957 size = GetDDFileSize(dde, g);
3958 fib->fib_DirEntryType = \
3959 fib->fib_EntryType = ST_FILE;
3960 fib->fib_Protection = rext->blk.dd_protection;
3961 fib->fib_Size = GetDDFileSize32(dde, g);
3962 fib->fib_NumBlocks = (size / BLOCKSIZE) + (size % BLOCKSIZE > 0);
3963 fib->fib_Date.ds_Days = dde->creationday;
3964 fib->fib_Date.ds_Minute = dde->creationminute;
3965 fib->fib_Date.ds_Tick = dde->creationtick;
3966 fib->fib_Comment[0] = 0;
3967 fib->fib_OwnerUID = rext->blk.dd_uid;
3968 fib->fib_OwnerGID = rext->blk.dd_gid;
3970 /* get filename */
3971 nameptr = &fib->fib_FileName[1];
3972 strncpy(nameptr, &dde->filename[1], dde->filename[0]);
3973 nameptr += dde->filename[0];
3974 *nameptr++ = DELENTRY_SEP;
3976 /* append number appendix */
3977 fib->fib_DiskKey = slotnr;
3979 for (i = stcu_d(appbuffer, fib->fib_DiskKey); i < 3; i++)
3980 *nameptr++ = '0';
3981 strcpy(nameptr, appbuffer);
3982 fib->fib_FileName[0] = strlen(&fib->fib_FileName[1]);
3986 * Get a >valid< deldirentry starting from deldirentrynr ddnr
3987 * deldir is assumed present and enabled
3989 static struct deldirentry *GetDeldirEntry(IPTR *ddnr, globaldata * g)
3991 struct crootblockextension *rext = g->currentvolume->rblkextension;
3992 struct cdeldirblock *ddblk;
3993 struct deldirentry *dde;
3994 UWORD maxdelentrynr = rext->blk.deldirsize*DELENTRIES_PER_BLOCK - 1;
3995 UWORD oldlock;
3997 while (*ddnr <= maxdelentrynr)
3999 /* get deldirentry */
4000 if (!(ddblk = GetDeldirBlock(*ddnr/DELENTRIES_PER_BLOCK, g)))
4001 break;
4003 oldlock = ddblk->used;
4004 LOCK(ddblk);
4005 dde = &ddblk->blk.entries[*ddnr%DELENTRIES_PER_BLOCK];
4007 /* check if dde valid */
4008 if (IsDelfileValid(dde, ddblk, g))
4010 /* later --> check if blocks retaken !! */
4011 /* can be done by scanning bitmap!! */
4012 return dde;
4015 (*ddnr)++;
4016 ddblk->used = oldlock;
4019 /* nothing found */
4020 return NULL;
4024 * Get deldirentry deldirentrynr (NO CHECK ON VALIDITY
4025 * deldir is assumed present and enabled
4027 struct deldirentry *GetDeldirEntryQuick(ULONG ddnr, globaldata *g)
4029 struct cdeldirblock *ddblk;
4031 /* get deldirentry */
4032 if (!(ddblk = GetDeldirBlock(ddnr/DELENTRIES_PER_BLOCK, g)))
4033 return NULL;
4035 return &ddblk->blk.entries[ddnr%DELENTRIES_PER_BLOCK];
4038 static ULONG FillInDDEData(struct ExAllDataEXT *buffer, LONG type,
4039 struct deldirentry *dde, ULONG ddenr, ULONG spaceleft, globaldata *g)
4041 UWORD nameoffset, commentoffset = 0;
4042 UBYTE *nameptr;
4043 UCOUNT size;
4044 unsigned i;
4045 UBYTE appbuffer[6];
4047 /* get location to put name */
4048 switch (type)
4050 case ED_NAME:
4051 size = offsetof(struct ExAllDataEXT, ed_Type);
4052 break;
4053 case ED_TYPE:
4054 size = offsetof(struct ExAllDataEXT, ed_Size);
4055 break;
4056 case ED_SIZE:
4057 size = offsetof(struct ExAllDataEXT, ed_Prot);
4058 break;
4059 case ED_PROTECTION:
4060 size = offsetof(struct ExAllDataEXT, ed_Days);
4061 break;
4062 case ED_DATE:
4063 size = offsetof(struct ExAllDataEXT, ed_Comment);
4064 break;
4065 case ED_COMMENT:
4066 size = offsetof(struct ExAllDataEXT, ed_OwnerUID);
4067 break;
4068 case ED_OWNER:
4069 #if EXTENDED_PACKETS_MORPHOS
4070 size = offsetof(struct ExAllDataEXT, ed_Size64);
4071 break;
4072 case ED_SIZE64:
4073 #endif
4074 size = sizeof(struct ExAllDataEXT);
4075 break;
4076 default:
4077 size = offsetof(struct ExAllDataEXT, ed_Type);
4078 break;
4081 /* size of name */
4082 nameoffset = size;
4083 size += dde->filename[0] + 4 + 1; /* extra ddenr (3+1) and termination zero (1) */
4085 /* size of comment */
4086 if (type >= ED_COMMENT)
4088 commentoffset = size;
4089 size++;
4092 /* check fit */
4093 size = (size + 1) & 0xfffe;
4094 if (size > spaceleft)
4095 return 0;
4097 /* copy */
4098 buffer->ed_Next = NULL;
4099 switch (type)
4101 #if EXTENDED_PACKETS_MORPHOS
4102 case ED_SIZE64:
4103 buffer->ed_Size64 = GetDDFileSize(dde, g);
4104 #endif
4106 case ED_OWNER:
4107 buffer->ed_OwnerUID = g->currentvolume->rblkextension->blk.dd_uid;
4108 buffer->ed_OwnerGID = g->currentvolume->rblkextension->blk.dd_gid;
4110 case ED_COMMENT:
4111 buffer->ed_Comment = (UBYTE *)buffer + commentoffset;
4112 *((UBYTE *)buffer + commentoffset) = 0x0;
4114 case ED_DATE:
4115 buffer->ed_Days = dde->creationday;
4116 buffer->ed_Mins = dde->creationminute;
4117 buffer->ed_Ticks = dde->creationtick;
4119 case ED_PROTECTION:
4120 buffer->ed_Prot = g->currentvolume->rblkextension->blk.dd_protection;
4122 case ED_SIZE:
4123 buffer->ed_Size = GetDDFileSize32(dde, g);
4125 case ED_TYPE:
4126 buffer->ed_Type = ST_FILE;
4128 case ED_NAME:
4129 /* filename */
4130 nameptr = (UBYTE *)buffer + nameoffset;
4131 strncpy(nameptr, &dde->filename[1], dde->filename[0]);
4132 nameptr += dde->filename[0];
4133 *nameptr++ = DELENTRY_SEP;
4135 /* append nr */
4136 for (i = stcu_d(appbuffer, ddenr); i < 3; i++)
4137 *nameptr++ = '0';
4138 strcpy(nameptr, appbuffer);
4140 default:
4141 buffer->ed_Name = (UBYTE *)buffer + nameoffset;
4144 return size;
4148 static struct cdeldirblock *GetDeldirBlock(UWORD seqnr, globaldata *g)
4150 struct volumedata *volume = g->currentvolume;
4151 struct crootblockextension *rext;
4152 struct cdeldirblock *ddblk;
4153 ULONG blocknr;
4155 rext = volume->rblkextension;
4157 if (seqnr > MAXDELDIR)
4159 DB(Trace(5,"GetDeldirBlock","seqnr out of range = %lx\n", seqnr));
4160 ErrorMsg (AFS_ERROR_DELDIR_INVALID, NULL, g);
4161 return NULL;
4164 /* get blocknr */
4165 if (!(blocknr = rext->blk.deldir[seqnr]))
4167 DB(Trace(5,"GetDeldirBlock","ERR: index zero\n"));
4168 ErrorMsg (AFS_ERROR_DELDIR_INVALID, NULL, g);
4169 return NULL;
4172 /* check cache */
4173 for (ddblk = HeadOf(&volume->deldirblks); ddblk->next; ddblk=ddblk->next)
4175 if (ddblk->blk.seqnr == seqnr)
4177 MakeLRU (ddblk);
4178 return ddblk;
4182 /* alloc cache */
4183 if (!(ddblk = (struct cdeldirblock *)AllocLRU(g)))
4185 DB(Trace(5,"GetDeldirBlock","ERR: alloclru failed\n"));
4186 return NULL;
4189 /* read block */
4190 if (RawRead ((UBYTE*)&ddblk->blk, RESCLUSTER, blocknr, g) != 0)
4192 DB(Trace(5,"GetDeldirBlock","Read ERR: seqnr = %d blocknr = %lx\n", seqnr, blocknr));
4193 FreeLRU ((struct cachedblock *)ddblk);
4194 return NULL;
4197 /* check it */
4198 if (ddblk->blk.id != DELDIRID)
4200 ErrorMsg (AFS_ERROR_DELDIR_INVALID, NULL, g);
4201 FreeLRU ((struct cachedblock *)ddblk);
4202 volume->rootblk->options ^= MODE_DELDIR;
4203 g->deldirenabled = FALSE;
4206 /* initialize it */
4207 ddblk->volume = volume;
4208 ddblk->blocknr = blocknr;
4209 ddblk->used = FALSE;
4210 ddblk->changeflag = FALSE;
4212 /* add to cache and return */
4213 MinAddHead (&volume->deldirblks, ddblk);
4214 return ddblk;
4217 struct cdeldirblock *NewDeldirBlock(UWORD seqnr, globaldata *g)
4219 struct volumedata *volume = g->currentvolume;
4220 struct crootblockextension *rext;
4221 struct cdeldirblock *ddblk;
4222 ULONG blocknr;
4224 rext = volume->rblkextension;
4226 if (seqnr > MAXDELDIR)
4228 DB(Trace(5, "NewDelDirBlock", "seqnr out of range = %lx\n", seqnr));
4229 return NULL;
4232 /* alloc block and LRU slot */
4233 if (!(ddblk = (struct cdeldirblock *)AllocLRU(g)) ||
4234 !(blocknr = AllocReservedBlock(g)) )
4236 if (ddblk)
4237 FreeLRU((struct cachedblock *)ddblk);
4238 return NULL;
4241 /* make reference */
4242 rext->blk.deldir[seqnr] = blocknr;
4244 /* fill block */
4245 ddblk->volume = volume;
4246 ddblk->blocknr = blocknr;
4247 ddblk->used = FALSE;
4248 ddblk->blk.id = DELDIRID;
4249 ddblk->blk.seqnr = seqnr;
4250 ddblk->changeflag = TRUE;
4251 ddblk->blk.protection = DELENTRY_PROT; /* re..re..re.. */
4252 ddblk->blk.creationday = volume->rootblk->creationday;
4253 ddblk->blk.creationminute = volume->rootblk->creationminute;
4254 ddblk->blk.creationtick = volume->rootblk->creationtick;
4256 /* add to cache and return */
4257 MinAddHead(&volume->deldirblks, ddblk);
4258 return ddblk;
4261 /* Allocate deldirslot. Free anodechain attached to slot and clear it.
4262 * An intermediate update is possible, due to FreeAnodesInChain()
4264 static int AllocDeldirSlot(globaldata * g)
4266 struct crootblockextension *rext = g->currentvolume->rblkextension;
4267 struct cdeldirblock *ddblk;
4268 struct deldirentry *dde;
4269 int ddnr = 0;
4270 ULONG anodenr;
4272 /* get deldirentry and update roving ptr */
4273 ddnr = rext->blk.deldirroving;
4274 if (!(ddblk = GetDeldirBlock(ddnr/DELENTRIES_PER_BLOCK,g)))
4276 rext->blk.deldirroving = 0;
4277 return 0;
4280 dde = &ddblk->blk.entries[ddnr%DELENTRIES_PER_BLOCK];
4281 rext->blk.deldirroving = (rext->blk.deldirroving + 1) %
4282 (rext->blk.deldirsize*DELENTRIES_PER_BLOCK);
4283 MakeBlockDirty((struct cachedblock *)ddblk, g);
4285 anodenr = dde->anodenr;
4286 if (anodenr)
4288 /* clear it for reuse */
4289 dde->anodenr = 0;
4291 /* free attached anodechain */
4292 FreeAnodesInChain(anodenr, g); /* only FREE anodes, not blocks!! */
4295 DB(Trace(1, "AllocDelDirSlot", "Allocate slot %ld\n", ddnr));
4296 return ddnr;
4300 /* Add a file to the deldir.
4301 * Deldir assumed enabled here, and info assumed a file (ST_FILE)
4302 * ddnr is deldir slot to use. Slot is assumed to be allocated by
4303 * AllocDeldirSlot()
4305 static void AddToDeldir(union objectinfo *info, int ddnr, globaldata * g)
4307 struct cdeldirblock *ddblk;
4308 struct deldirentry *dde;
4309 struct direntry *de = info->file.direntry;
4310 struct crootblockextension *rext;
4311 struct DateStamp time;
4313 DB(Trace(1, "AddToDeldir", "slotnr %ld\n", ddnr));
4314 /* get deldirentry to put it in */
4315 ddblk = GetDeldirBlock(ddnr/DELENTRIES_PER_BLOCK, g);
4316 dde = &ddblk->blk.entries[ddnr%DELENTRIES_PER_BLOCK];
4318 /* put new one in */
4319 dde->anodenr = de->anode;
4320 SetDDFileSize(dde, GetDEFileSize(de, g), g);
4321 dde->creationday = de->creationday;
4322 dde->creationminute = de->creationminute;
4323 dde->creationtick = de->creationtick;
4324 dde->filename[0] = min(DELENTRYFNSIZE - 1, de->nlength);
4325 strncpy(&dde->filename[1], &de->startofname, dde->filename[0]);
4327 /* Touch deldir block. Inserted here, simply because this the only
4328 * place touching the deldir will be needed.
4329 * Note: Only this copy is touched ...
4331 DateStamp(&time);
4332 rext = g->currentvolume->rblkextension;
4333 ddblk->blk.creationday = rext->blk.dd_creationday = (UWORD)time.ds_Days;
4334 ddblk->blk.creationminute = rext->blk.dd_creationminute = (UWORD)time.ds_Minute;
4335 ddblk->blk.creationtick = rext->blk.dd_creationtick = (UWORD)time.ds_Tick;
4337 /* dirtify block */
4338 MakeBlockDirty((struct cachedblock *)ddblk, g);
4341 /* Set number of deldir blocks (Has to be single threaded)
4342 * If 0 then deldir is disabled (but MODE_DELDIR stays;
4343 * InitModules() detect that the number of deldirblocks is 0)
4344 * There must be a currentvolume
4345 * Returns error (0 = success)
4347 ULONG SetDeldir(int nbr, globaldata *g)
4349 struct crootblockextension *rext = g->currentvolume->rblkextension;
4350 struct cdeldirblock *ddblk, *next;
4351 listentry_t *list;
4352 int i;
4353 ULONG error = 0;
4355 /* check range */
4356 if (nbr < 0 || nbr > MAXDELDIR+1)
4357 return ERROR_BAD_NUMBER;
4359 /* check if there are locks on any deldir, delfile */
4360 for (list = HeadOf(&g->currentvolume->fileentries); list->next; list=list->next)
4362 if (IsDelDir(list->info) || IsDelFile(list->info))
4363 return ERROR_OBJECT_IN_USE;
4366 UpdateDisk(g);
4368 /* flush cache */
4369 for (ddblk = HeadOf(&g->currentvolume->deldirblks); (next=ddblk->next); ddblk=next)
4371 FlushBlock((struct cachedblock *)ddblk, g);
4372 MinRemove(LRU_CHAIN(ddblk));
4373 MinAddHead(&g->glob_lrudata.LRUpool, LRU_CHAIN(ddblk));
4374 // i.p.v. FreeLRU((struct cachedblock *)ddblk, g);
4377 /* free unwanted deldir blocks */
4378 for (i = nbr; i < rext->blk.deldirsize; i++)
4380 FreeReservedBlock(rext->blk.deldir[i], g);
4381 rext->blk.deldir[i] = 0;
4384 /* allocate wanted ones */
4385 for (i = rext->blk.deldirsize; i < nbr; i++)
4387 if (!NewDeldirBlock(i,g))
4389 nbr = i+1;
4390 error = ERROR_DISK_FULL;
4391 break;
4395 /* if deldir size increases, start roving in a the new area
4396 * if deldir size decreases, start roving from the start
4398 if (nbr > rext->blk.deldirsize)
4399 rext->blk.deldirroving = rext->blk.deldirsize * DELENTRIES_PER_BLOCK;
4400 else
4401 rext->blk.deldirroving = 0;
4403 /* enable/disable */
4404 rext->blk.deldirsize = nbr;
4405 g->deldirenabled = (nbr > 0);
4407 MakeBlockDirty((struct cachedblock *)rext, g);
4408 UpdateDisk(g);
4409 return error;
4411 #endif