1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
|
# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
include <knet.h>
include <ctype.h>
include <mach.h>
include <error.h>
include <syserr.h>
include <config.h>
include <fio.h>
.help vfnmap
.nf ___________________________________________________________________________
VFNMAP -- A package for mapping virtual filenames to and from OS filenames.
The abstract datatype dealt with here is the VFN. The operations defined for
a VFN are [1] map to OSFN, [2] add a new VFN to the VFN database, and [3] delete
a VFN from the VFN database. The VFN database is manipulated only by this
package. This is an internal package, not a user package -- the semantics of
locking parts of the VFN database are delicate.
A VFN must be opened separately for each file to be accessed, except when
reading a directory in which case the vfnmap must be opened separately for
each directory to be scanned. Only a single VFN may be opened for writing by
a process at any one time (any number of VFN's, including directories, may be
opened for reading at any one time). The mapping file is not physically opened
unless the escape sequence encoded filename is degenerate. The mapping file is
locked only if the vfn is degenerate and the access mode is VFN_WRITE. The
recognized vfn access modes are VFN_READ, VFN_WRITE, and VFN_UNMAP (for reading
directories).
It is intended that THE VFN WILL BE OPENED FOR ONLY A BRIEF PERIOD OF TIME TO
MINIMIZE THE AMOUNT OF TIME THAT THE MAPPING FILE IS LOCKED. Furthermore,
while the VFN is locked we must avoid any operations that involve waiting for
system resources and hence introduce the possibility of deadlock.
vp = vfnopen (vfn, mode)
vfnclose (vp, update)
stat = vfnmap (vp, osfn, maxch)
stat = vfnadd (vp, osfn, maxch)
stat = vfndel (vp, osfn, maxch)
stat = vfnunmap (vp, osfn, vfn, maxch)
fmapfn (vfn, osfn, maxch) [=:vfnopen/RO,vfnmap,vfnclose]
A distinction is made between mapping the filename and opening and closing
the vfn to permit efficient and secure error recovery. The mapping file is
not updated on disk until the physical file operation (create, delete, etc)
has succeeded. If the operation fails vfnclose is called with VFN_NOUPDATE
and the mapping file is not touched. If the vfn was opened VFN_READ the
update flag is ignored. No vfn disk data structures will be modified if a
vfn is closed with VFN_NOUPDATE set. If updating is enabled, ".zmd" dependency
files may be created or deleted, the mapping file may be created, deleted,
or updated.
The VFNMAP, VFNADD, VFNDEL, and VFNUNMAP procedures all perform a mapping
operation, returning OK if the filename could be mapped and ERR if the
mapping fails and no OSFN or VFN is returned. A VFNMAP, VFNADD, or VFNDEL
mapping can only return ERR if the VFN is degenerate and either no entry
was found in the mapping file (VFNMAP, VFNDEL) or there already was an entry
(VFNADD). OSFN is returned as a packed string, VFN as a normal string.
NOTE1 -- (Dec84) The "degeneracy flag files" are no longer used, but some of
the code has been left in place, to avoid having to modify and test the code
after its removal. This code should be removed when other modifications are
required which will require careful testing of the package.
NOTE2 -- Interrupts and automatic error checking should be disabled while a
VFN is open to prevent corruption of the mapping file, failure to remove a
file lock, or failure to close the mapping file.
.endhelp ______________________________________________________________________
define SZ_VFN 255 # max chars in V_VFN field
define LEN_FN 128 # no. chars allocated to VFNFN field
define SZ_FNPAIR (LEN_FN*2) # size of filename pair 2(+EOS+align)
define MAX_LONGFNAMES 100 # max filename pairs in FNMAP
define SZ_ZFD 4 # size of ".zfd" extension
define MAX_READS 5 # max trys to read mapping file
define MAX_DEGENERACY 50 # max VFN's mapping to same OSFN
define MAX_DIGITS 2 # max digits in degeneracy index
define V_MAP 1 # VFN opcodes
define V_ADD 2
define V_DEL 3
define V_UNMAP 4
# VFD -- VFN descriptor structure. Assumes an 80 char (or less) OSDIR field
# and 35 char (or less) VFN, ROOT and EXTN fields (see fio.h).
define LEN_VFD 778
define V_MFD Memi[$1] # ptr to mapping file descriptor
define V_ACMODE Memi[$1+1] # access mode
define V_LENOSDIR Memi[$1+2] # length of OSDIR string
define V_LENROOT Memi[$1+3] # length of ROOT string
define V_LENEXTN Memi[$1+4] # length of EXTN string
define V_LONGROOT Memi[$1+5] # root field exceeds OS limit
define V_VFN Memc[P2C($1+10)] # VFN - ldir
define V_OSDIR Memc[P2C($1+266)] # OS directory
define V_ROOT Memc[P2C($1+522)] # OS root filename
define V_EXTN Memc[P2C($1+650)] # OS extension
# MFD -- Mapping file descriptor structure. An upper limit is placed on
# the number of filename pairs in the descriptor because it is assumed that
# long filenames are rare. Note that this places a limit on the number of long
# filenames in the directory, not on the number of files in the directory.
# If this is a problem the code is not difficult to generalize.
define LEN_MFD (250+MAX_LONGFNAMES*SZ_FNPAIR/SZ_STRUCT)
define MIN_LENMFD (250+1*SZ_FNPAIR/SZ_STRUCT)
define SZ_MAPFNAME (240*SZ_STRUCT-1)
define M_CHECKSUM Memi[$1] # checksum of file when written
define M_CHAN Memi[$1+1] # OS channel of mapping file
define M_LOCKTIME Meml[$1+2] # clktime when lock set
define M_NFILES Memi[$1+3] # no. filename pairs in map
define M_LASTOP Memi[$1+4] # code for last op on database
define M_MODIFIED Memi[$1+5] # YES if database modified
define M_ADDZMD Memi[$1+6] # create .zmd file at update
define M_DELZMD Memi[$1+7] # delete .zmd file at update
define M_MAPFNAME Memc[P2C($1+10)] # name of map file
define M_FNMAP (P2C($1+250)) # filename pairs
# Subscript the (VFN,OSFN) filename pairs. For example, FN_VFN(mfd,n)
# references the VFN field of filename pair N of the mapping file MFD.
define FN_VFN Memc[M_FNMAP($1)+(($2)*2-2)*LEN_FN]
define FN_OSFN Memc[M_FNMAP($1)+(($2)*2-1)*LEN_FN]
# VFNOPEN -- Open a VFN. Allocate VFD and convert the VFN into OSFN, ROOT,
# and EXTN fields. The EXTN field is mapped to the OS extension, but the
# ROOT field may be longer than is permitted by the OS. The mapping file
# is not referenced until the OSFN is requested in a map, add, or del op.
pointer procedure vfnopen (vfn, mode)
char vfn[ARB] # virtual filename
int mode # access mode for VFN database
bool first_time
int n_open_vfns, root_offset, extn_offset
pointer def_vfd, vfd
data first_time /true/
common /vfncom/ n_open_vfns
errchk syserrs, malloc, calloc, vfn_translate, vvfn_readmapfile
begin
# After the first call a single VFD will be allocated at all times.
# This eliminates the need to allocate and free a descriptor in each
# call.
if (first_time) {
call malloc (def_vfd, LEN_VFD, TY_STRUCT)
n_open_vfns = 0
first_time = false
}
# Allocate and initialize the VFD.
if (n_open_vfns <= 0) {
vfd = def_vfd
call aclri (Memi[vfd], LEN_VFD)
} else
call calloc (vfd, LEN_VFD, TY_STRUCT)
n_open_vfns = n_open_vfns + 1
# Break the VFN into its component parts. Map using escape sequence
# encoding, but do not squeeze the OSFN. Most calls are read only
# accesses that do not involve accessing the VFN database. The
# following is what takes all the time (string concatenation and
# packing in VFNMAP is also a factor).
call vfn_translate (vfn, V_OSDIR(vfd), V_LENOSDIR(vfd),
V_ROOT(vfd), V_LENROOT(vfd),
V_EXTN(vfd), V_LENEXTN(vfd))
# Determine whether the length of the root exceeds the max host system
# filename length, and set flag if so. If longroot, squeeze the root
# because the unsqueezed root is not useful for anything. The V_VFN
# field is used as a temporary.
if (V_LENROOT(vfd) > MAX_ROOTLEN) {
call vfn_squeeze (V_ROOT(vfd), V_VFN(vfd), MAX_ROOTLEN)
call strcpy (V_VFN(vfd), V_ROOT(vfd), MAX_ROOTLEN)
V_LENROOT(vfd) = MAX_ROOTLEN
V_LONGROOT(vfd) = YES
} else
V_LONGROOT(vfd) = NO
# Set access mode and save VFN.
V_ACMODE(vfd) = mode
switch (mode) {
case VFN_READ, VFN_WRITE:
call zfnbrk (vfn, root_offset, extn_offset)
call strcpy (vfn[root_offset], V_VFN(vfd), SZ_VFN)
case VFN_UNMAP:
call vvfn_readmapfile (vfd)
default:
call syserrs (SYS_FVFNMODE, vfn)
}
return (vfd)
end
# VFNMAP -- Map and pack the VFN into an OSFN, but do not modify the database.
# The mapping file is accessed only if the OS filename is degenerate, i.e.,
# if the directory contains more than one VFN mapping to the same OSFN after
# escape sequence encoding and squeezing.
int procedure vfnmap (vfd, osfn, maxch)
pointer vfd # pointer to VFD descriptor
char osfn[ARB] # char buffer to receive packed OSFN
int maxch
int status
int vfnmapu()
begin
status = vfnmapu (vfd, osfn, maxch)
call osfn_pkfname (osfn, osfn, maxch)
return (status)
end
# VFNMAPU -- Map but do not pack a VFN into an OSFN. Call VFNMAP if you want
# a packed osfn.
int procedure vfnmapu (vfd, osfn, maxch)
pointer vfd # pointer to VFD descriptor
char osfn[maxch] # char buffer to receive unpacked OSFN
int maxch
int op, status
int gstrcpy(), vfn_getosfn()
errchk vfn_getosfn, vvfn_readmapfile
define degenerate_ 91
begin
# The OSDIR and ROOT fields are used twice below, so we concatenate
# them here.
op = gstrcpy (V_OSDIR(vfd), osfn, maxch) + 1
op = op + gstrcpy (V_ROOT(vfd), osfn[op], maxch-op+1)
# If the root field of the osfn is within the length limit for a host
# system filename all we have to do is concatenate and pack, returning
# the packed osfn. If the root has been squeezed we have to look to
# see if it is unique within the directory; if it is then we do not
# have to read the mapping file. Filename mapping is fast provided
# we do not have to read the mapping file.
if (V_LONGROOT(vfd) == YES)
goto degenerate_
# Concatenate the final osfn.
if (V_LENEXTN(vfd) > 0 && op < maxch) {
osfn[op] = EXTN_DELIMITER
op = op + 1
call strcpy (V_EXTN(vfd), osfn[op], maxch-op+1)
} else
osfn[op] = EOS
return (OK)
degenerate_
# If we get here then the squeezed filename is degenerate and we have
# to read the mapping file to get the OSFN assigned by VFNADD. If the
# mapping file does not exist and the VFN is open with write perm,
# then we were probably called by VFNADD and we go ahead and create
# a new mapping file.
call vvfn_readmapfile (vfd)
# Search the file name list for the named VFN.
if (vfn_getosfn (vfd, V_VFN(vfd), osfn, maxch) <= 0)
status = ERR
else
status = OK
M_LASTOP(V_MFD(vfd)) = V_MAP
return (status)
end
# VFNADD -- Map a VFN to an OSFN and add the VFN,OSFN pair to the VFN database
# if the OSFN is long. An entry must be made whether or not the filename is
# degenerate, to permit the inverse mapping.
int procedure vfnadd (vfd, osfn, maxch)
pointer vfd # pointer to VFN descriptor
char osfn[maxch] # buffer to receive packed OSFN
int maxch
int file_exists
int vfnmap(), vfn_enter()
errchk vfnmap
begin
# Call VFNMAP to perform the mapping and possibly open the database.
# If VFNMAP returns ERR then the filename was degenerate but was not
# found in the database, which is what we want since we are adding
# the file. We return ERR if the file already exists, whether or
# not the name is degenerate.
if (vfnmap (vfd, osfn, maxch) == ERR) {
# Long filename but no entry found in database; we have to add
# a new entry.
return (vfn_enter (vfd, osfn, maxch))
} else if (V_LONGROOT(vfd) == NO) {
# Short filename; see if physical file exists.
call zfacss (osfn, 0, 0, file_exists)
if (file_exists == YES)
return (ERR)
else
return (OK)
} else
# VFN found in database and filename is long.
return (ERR)
end
# VFNDEL -- Map a VFN to an OSFN and delete the VFN,OSFN pair from the VFN
# database if the OSFN is long.
int procedure vfndel (vfd, osfn, maxch)
pointer vfd # pointer to VFN descriptor
char osfn[maxch] # buffer to receive packed OSFN
int maxch
char first_char
int fn, fn_index, ip, junk
pointer sp, root, extn, mfd, vfnp
bool streq()
int vfnmap()
errchk vfnmap
begin
call smark (sp)
call salloc (root, SZ_VFNFN, TY_CHAR)
call salloc (extn, SZ_VFNFN, TY_CHAR)
# Call VFNMAP to perform the mapping and possibly open the database.
# If VFNMAP returns ERR then the filename was degenerate but was not
# found in the database and we are done. If VFNMAP returns OK we
# are done unless the filename is long.
if (vfnmap (vfd, osfn, maxch) == ERR) {
# Long filename but no entry found in database; nothing to delete.
call sfree (sp)
return (ERR)
} else if (V_LONGROOT(vfd) == NO) {
# Short filename; nothing to delete but it is not an error.
call sfree (sp)
return (OK)
}
# If we get here the VFN was found in the database and the filename
# is long. Locate the VFN entry and determine if there are any
# other entries mapping to the same squeezed root.
mfd = V_MFD(vfd)
vfnp = M_FNMAP(mfd)
first_char = V_VFN(vfd)
fn_index = 0
M_DELZMD(mfd) = YES
do fn = 1, M_NFILES(mfd) {
if (Memc[vfnp] == first_char) {
if (fn_index == 0)
if (streq (Memc[vfnp], V_VFN(vfd)))
fn_index = fn
ip = 1
call vfn_encode (Memc[vfnp], ip, Memc[root], junk, Memc[extn],
junk)
if (streq (Memc[root], V_ROOT(vfd))) {
M_DELZMD(mfd) = NO
if (fn_index != 0)
break
}
}
vfnp = vfnp + SZ_FNPAIR
}
# Delete the filename pair from the database. Deletion is effected by
# shifting the higher indexed filename pairs back one filepair.
# We are more concerned here about saving space in the mapping file
# and in the MFD, than in making set deletion efficient.
for (fn = fn_index + 1; fn <= M_NFILES(mfd); fn=fn+1)
call amovc (FN_VFN(mfd,fn), FN_VFN(mfd,fn-1), SZ_FNPAIR)
M_NFILES(mfd) = M_NFILES(mfd) - 1
M_LASTOP(mfd) = V_DEL
M_MODIFIED(mfd) = YES
call sfree (sp)
return (OK)
end
# VFNUNMAP -- Convert an OSFN into a VFN. Search the MFD file list for the
# named OSFN, and if found return the associated VFN as an output argument and
# the length of the VFN string as the function value. If entry is not found
# perform the inverse transformation (map extension, invert escape sequence
# encoding). The VFN returned does not include a logical directory prefix.
# This function is called to perform the inverse mapping when reading
# directories.
int procedure vfnunmap (vfd, osfn, vfn, maxch)
pointer vfd # VFN descriptor
char osfn[maxch] # OS filename to be searched for (packed)
char vfn[ARB] # receives unpacked VFN
int maxch
char first_char
int fn, op, extn_offset
pointer mfd, osfnp, sp, osfname, ip
bool streq()
int gstrcpy(), vfn_decode()
begin
call smark (sp)
call salloc (osfname, SZ_PATHNAME, TY_CHAR)
call strupk (osfn, Memc[osfname], SZ_PATHNAME)
if (CASE_INSENSITIVE && HOST_CASE != 'L')
call strlwr (Memc[osfname])
# Search mapping file for OSFN and return VFN if found.
mfd = V_MFD(vfd)
osfnp = M_FNMAP(mfd) + LEN_FN
first_char = Memc[osfname]
do fn = 1, M_NFILES(mfd) {
if (Memc[osfnp] == first_char)
if (streq (Memc[osfnp], Memc[osfname])) {
call sfree (sp)
return (gstrcpy (FN_VFN(mfd,fn), vfn, maxch))
}
osfnp = osfnp + SZ_FNPAIR
}
# No entry in mapping file, so we must perform the inverse
# transformation. Decode the root, unmap and decode the extension,
# and return VFN. If there are multiple EXTN_DELIMITER delimited
# fields only the final one is mapped as an extension, but all are
# decoded.
vfn[1] = EOS
extn_offset = 0
ip = osfname
op = 1
while (Memc[ip] != EOS) {
op = op + vfn_decode (Memc, ip, vfn[op], maxch-op+1)
if (Memc[ip] == EXTN_DELIMITER) {
ip = ip + 1
vfn[op] = '.'
op = op + 1
vfn[op] = EOS
extn_offset = op
}
}
# Add mapped filename extension. If the OS extension maps into a
# null VFN extension omit the trailing period. If the . is preceded
# by another dot it is not considered an extension delimiter.
if (extn_offset > 0) {
call vfn_unmap_extension (vfn[extn_offset], vfn[extn_offset],
SZ_VFNFN - extn_offset + 1)
if (vfn[extn_offset] != EOS) {
for (op=extn_offset; vfn[op] != EOS; op=op+1)
;
} else if (extn_offset<=2 || vfn[extn_offset-2] == EXTN_DELIMITER) {
op = extn_offset
} else {
vfn[extn_offset-1] = EOS
op = extn_offset - 1
}
}
call sfree (sp)
return (op - 1)
end
# VFNCLOSE -- Close a VFN. Update the VFN database if the MFD has been
# modified and updating is enabled. Release the lock on the directory and
# return all storage.
procedure vfnclose (vfd, update_enable)
pointer vfd # VFN descriptor
int update_enable # update the database?
int n_open_vfns, lastop, junk, len_struct
int status
pointer sp, fname, osfn, mfd
int osfn_unlock(), osfn_timeleft()
int vfnadd(), vfndel(), vvfn_checksum()
common /vfncom/ n_open_vfns
errchk osfn_unlock, osfn_timeleft, vfnadd, vfndel, syserrs
define freemfd_ 91
define freevfd_ 92
define unlock_ 93
begin
call smark (sp)
call salloc (fname, SZ_PATHNAME, TY_CHAR)
call salloc (osfn, SZ_PATHNAME, TY_CHAR)
# If the mapping file was never referenced or the database was not
# modified in the MFD, just return buffers and quit.
mfd = V_MFD(vfd)
n_open_vfns = n_open_vfns - 1
if (mfd == NULL)
goto freevfd_
else if (M_MODIFIED(mfd) == NO || update_enable == VFN_NOUPDATE) {
if (V_ACMODE(vfd) == VFN_WRITE)
goto unlock_
else
goto freemfd_
}
# If we get here then the mapping file is open with write permission,
# a transaction has been performed which modified the database, and we
# were called with updating enabled. If there is not enough time
# remaining on the lock to permit the update, rollback (repeat) the
# last transaction, otherwise update the database on disk.
call osfn_pkfname (M_MAPFNAME(mfd), Memc[osfn], SZ_PATHNAME)
while (osfn_timeleft (Memc[osfn], M_LOCKTIME(mfd)) < MIN_TIMELEFT) {
# Rollback transaction. Hopefully it wont take so long this time
# (should only take a second or so).
junk = osfn_unlock (Memc[osfn], M_LOCKTIME(mfd))
lastop = M_LASTOP(mfd)
call mfree (mfd, TY_STRUCT)
switch (lastop) {
case V_ADD:
junk = vfnadd (vfd, Memc[fname], SZ_PATHNAME)
case V_DEL:
junk = vfndel (vfd, Memc[fname], SZ_PATHNAME)
}
}
# From here on we are committed. Update and close the mapping file.
# Add checksum to ensure correct reads.
len_struct = LEN_MFD - (MAX_LONGFNAMES - M_NFILES(mfd)) *
(SZ_FNPAIR / SZ_STRUCT)
M_CHECKSUM(mfd) = vvfn_checksum (Memi[mfd+1], (len_struct - 1) * SZ_INT)
call zawrbf (M_CHAN(mfd), Memi[mfd], len_struct * SZ_STRUCT * SZB_CHAR,
long(1))
call zawtbf (M_CHAN(mfd), status)
if (status == ERR)
call syserrs (SYS_FWRITE, M_MAPFNAME(mfd))
unlock_
call zclsbf (M_CHAN(mfd), status)
if (status == ERR)
call syserrs (SYS_FCLOSE, M_MAPFNAME(mfd))
# All done! Unlock the directory. If there are no files left in
# the mapping file, delete the file and all lock files.
call osfn_pkfname (M_MAPFNAME(mfd), Memc[osfn], SZ_PATHNAME)
if (M_NFILES(mfd) == 0) {
call zfdele (Memc[osfn], junk)
call osfn_rmlock (Memc[osfn])
} else if (osfn_unlock (Memc[osfn], M_LOCKTIME(mfd)) == ERR) {
iferr (call syserrs (SYS_FNOLOCK, M_MAPFNAME(mfd)))
call erract (EA_WARN)
}
freemfd_
call mfree (mfd, TY_STRUCT)
freevfd_
if (n_open_vfns > 0)
call mfree (vfd, TY_STRUCT)
call sfree (sp)
end
# VVFN_READMAPFILE -- Open and read the mapping file. In VFN_WRITE mode a
# new mapping file is created if necessary.
procedure vvfn_readmapfile (vfd)
pointer vfd # pointer to VFD descriptor
int new_struct_size, checksum, file_exists, maxbytes, new_mapping_file
int nbytes, len_file, junk, chan, ntrys, errnum, status
long locktime
pointer sp, mfd, fname, pkosfn
int vvfn_checksum()
long osfn_lock()
errchk calloc, syserrs, osfn_lock, osfn_init
define cleanup_ 91
define reallynew_ 92
begin
call smark (sp)
call salloc (fname, SZ_PATHNAME, TY_CHAR)
call salloc (pkosfn, SZ_PATHNAME, TY_CHAR)
call calloc (mfd, LEN_MFD, TY_STRUCT)
V_MFD(vfd) = mfd
# Make OSFN of mapping file. If the mode is VFN_UNMAP then the root
# field, if any, is the filename of the directory containing the
# mapping file.
call strcpy (V_OSDIR(vfd), M_MAPFNAME(mfd), SZ_MAPFNAME)
if (V_ACMODE(vfd) == VFN_UNMAP && V_LENROOT(vfd) > 0)
call zfsubd (M_MAPFNAME(mfd), SZ_MAPFNAME, V_ROOT(vfd), junk)
call strcat (FNMAPPING_FILE, M_MAPFNAME(mfd), SZ_MAPFNAME)
call strlwr (M_MAPFNAME(mfd))
call osfn_pkfname (M_MAPFNAME(mfd), Memc[fname], SZ_PATHNAME)
# Open or create mapping file. Create must precede lock as lock will
# abort if the file to be locked does not exist. OSFN_LOCK will call
# error if no write perm on directory. If file locking is implemented
# by host, open will return ERR if file is write locked by another
# process, in which case we wait until the file can be opened.
call zfacss (Memc[fname], 0, 0, file_exists)
new_mapping_file = NO
switch (V_ACMODE(vfd)) {
case VFN_WRITE:
# Determine whether or not the mapping file exists.
call osfn_pkfname (M_MAPFNAME(mfd), Memc[pkosfn], SZ_PATHNAME)
if (file_exists == YES) {
# Open an existing mapping file for exclusive access.
iferr (locktime = osfn_lock (Memc[pkosfn])) {
call mfree (mfd, TY_STRUCT)
V_MFD(vfd) = NULL
call erract (EA_ERROR)
}
repeat {
call zopnbf (Memc[fname], READ_WRITE, chan)
if (chan == ERR)
call zwmsec (1000)
} until (chan != ERR || !OS_FILELOCKING)
} else {
# Create a new mapping file and init the locks.
new_mapping_file = YES
call zopnbf (Memc[fname], NEW_FILE, chan)
if (chan != ERR) {
call osfn_initlock (Memc[pkosfn])
locktime = osfn_lock (Memc[pkosfn])
} else {
errnum = SYS_FOPEN
goto cleanup_
}
}
default:
if (file_exists == YES)
call zopnbf (Memc[fname], READ_ONLY, chan)
}
if (file_exists == YES && chan == ERR) {
errnum = SYS_FOPEN
goto cleanup_
}
# Read mapping file into descriptor. Repeat the read if the
# checksum is invalid, indicating that our read occurred while
# an update was in progress (locking need not lockout reads).
if (file_exists == YES) {
ntrys = 0
repeat {
# Read the file into the MFD.
maxbytes = LEN_MFD * SZ_STRUCT * SZB_CHAR
call zardbf (chan, Memi[mfd], maxbytes, long(1))
call zawtbf (chan, nbytes)
# The mapping file can be zero length if it was opened for
# updating but never written into.
if (nbytes == 0)
goto reallynew_
len_file = nbytes / SZB_CHAR / SZ_STRUCT
if (len_file < MIN_LENMFD) {
errnum = SYS_FREAD
goto cleanup_
}
# The checksum excludes the checksum field of MFD, but the
# entire MFD is written to the mapping file. Note that the
# file will contain garbage at the end following a file
# deletion (the file list gets shorter but the file does not).
# Compute checksum using only the valid file data, since that
# is how it is computed when the file is updated.
len_file = LEN_MFD - (MAX_LONGFNAMES - M_NFILES(mfd)) *
(SZ_FNPAIR / SZ_STRUCT)
checksum = vvfn_checksum (Memi[mfd+1], (len_file-1) * SZ_INT)
ntrys = ntrys + 1
} until (checksum == M_CHECKSUM(mfd) || ntrys > MAX_READS)
if (ntrys > MAX_READS) {
errnum = SYS_FVFNCHKSUM
goto cleanup_
}
}
reallynew_
# Close the mapping file if it is never going to be updated, and return
# any unused space in the mapping file descriptor.
if (V_ACMODE(vfd) != VFN_WRITE) {
if (file_exists == YES) {
call zclsbf (chan, status)
if (status == ERR)
call syserrs (SYS_FCLOSE, M_MAPFNAME(mfd))
}
new_struct_size = LEN_MFD -
(MAX_LONGFNAMES - M_NFILES(mfd)) * (SZ_FNPAIR/SZ_STRUCT)
call realloc (mfd, new_struct_size, TY_STRUCT)
V_MFD(vfd) = mfd
} else {
M_CHAN(mfd) = chan
M_LOCKTIME(mfd) = locktime
}
call sfree (sp)
return
cleanup_
call strcpy (M_MAPFNAME(mfd), Memc[fname], SZ_PATHNAME)
call mfree (mfd, TY_STRUCT)
V_MFD(vfd) = NULL
call syserrs (errnum, Memc[fname])
end
# VFN_ENTER -- Add a new filename pair to the mapping file. The VFN was not
# found in the database but that does not mean that there is not already an
# occurrence of the OSFN in the database and in the directory; if the OSFN is
# already in use, the filename is degenerate. If the OSFN exists in the
# directory then create ".zmd" degeneracy file and generate a unique OSFN,
# adding a new VFN,OSFN pair to the database.
int procedure vfn_enter (vfd, osfn, maxch)
pointer vfd # pointer to VFN descriptor
char osfn[maxch] # packaged OS filename (in/out)
int maxch
int file_exists, op, ndigits, m, n, num, offset, fn
pointer sp, fname, numbuf, mfd
int gstrcpy(), itoc()
errchk syserrs
begin
call smark (sp)
call salloc (fname, SZ_PATHNAME, TY_CHAR)
call salloc (numbuf, MAX_DIGITS, TY_CHAR)
# Generate the first attempt at the OSFN of the new file.
op = gstrcpy (V_OSDIR(vfd), Memc[fname], SZ_PATHNAME)
op = op + gstrcpy (V_ROOT(vfd), Memc[fname+op], SZ_PATHNAME-op)
if (V_LENEXTN(vfd) > 0) {
Memc[fname+op] = EXTN_DELIMITER
op = op + 1
call strcpy (V_EXTN(vfd), Memc[fname+op], SZ_PATHNAME-op)
}
offset = V_LENOSDIR(vfd) + 1
# Determine if a file already exists with the new OSFN. If so we
# must flag the file as degenerate and generate a unique OSFN.
call osfn_pkfname (Memc[fname], osfn, maxch)
call zfacss (osfn, 0, 0, file_exists)
if (file_exists == YES) {
# Set flag to create degeneracy flag file at update time.
M_ADDZMD(mfd) = YES
# Generate a unique OSFN for the new file. This is done by
# overwriting the 2nd and following characters of the root with
# a number until a unique name results. Nines are preferred as
# they occur least frequently in ordinary filenames.
for (m=0; file_exists == YES && m * 10 < MAX_DEGENERACY; m=m+1)
for (n=9; file_exists == YES && n >= 0; n=n-1) {
num = m * 10 + n
ndigits = itoc (num, Memc[numbuf], MAX_DIGITS)
call amovc (Memc[numbuf], Memc[fname+offset], ndigits)
call osfn_pkfname (Memc[fname], osfn, maxch)
call zfacss (osfn, 0, 0, file_exists)
}
if (m * 10 >= MAX_DEGENERACY)
call syserrs (SYS_FDEGEN, Memc[fname])
}
# Add the filename pair to the database. The directory prefix is
# omitted. If we run out of room in the mapping file we just abort.
mfd = V_MFD(vfd)
fn = M_NFILES(mfd) + 1
M_NFILES(mfd) = fn
if (fn > MAX_LONGFNAMES)
call syserrs (SYS_FTMLONGFN, Memc[fname])
# Save the VFN and OSFN, minus the directory prefix, in the mapping
# file structure.
call strcpy (V_VFN(vfd), FN_VFN(mfd,fn), SZ_VFNFN)
call strcpy (Memc[fname+offset-1], FN_OSFN(mfd,fn), SZ_VFNFN)
M_LASTOP(mfd) = V_ADD
M_MODIFIED(mfd) = YES
call sfree (sp)
return (OK)
end
# VFN_GETOSFN -- Search the MFD file list for the named VFN, and if found
# return the assigned OSFN as an output argument and the length of the OSFN
# string as the function value. ERR is returned if the entry cannot be found.
# The OSFN includes the OSDIR prefix.
int procedure vfn_getosfn (vfd, vfn, osfn, maxch)
pointer vfd # VFN descriptor
char vfn[ARB] # virtual filename to be searched for
char osfn[maxch] # receives unpacked OSFN
int maxch
char first_char
int fn, op
pointer mfd, vfnp
bool streq()
int gstrcpy()
begin
mfd = V_MFD(vfd)
vfnp = M_FNMAP(mfd)
first_char = vfn[1]
do fn = 1, M_NFILES(mfd) {
if (Memc[vfnp] == first_char)
if (streq (Memc[vfnp], vfn)) {
op = gstrcpy (V_OSDIR(vfd), osfn, maxch) + 1
op = op + gstrcpy (FN_OSFN(mfd,fn), osfn[op], maxch-op+1)
return (op - 1)
}
vfnp = vfnp + SZ_FNPAIR
}
return (ERR)
end
# VVFN_CHECKSUM -- Compute the integer checksum of a char array.
int procedure vvfn_checksum (a, nchars)
char a[nchars] # array to be summed
int nchars # length of array
int i, sum
begin
sum = 0
do i = 1, nchars
sum = sum + a[i]
return (sum)
end
|