Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/openjdk-multiarch-jdk8u
Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/sun/rmi/log/ReliableLog.java
38919 views
1
/*
2
* Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved.
3
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4
*
5
* This code is free software; you can redistribute it and/or modify it
6
* under the terms of the GNU General Public License version 2 only, as
7
* published by the Free Software Foundation. Oracle designates this
8
* particular file as subject to the "Classpath" exception as provided
9
* by Oracle in the LICENSE file that accompanied this code.
10
*
11
* This code is distributed in the hope that it will be useful, but WITHOUT
12
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14
* version 2 for more details (a copy is included in the LICENSE file that
15
* accompanied this code).
16
*
17
* You should have received a copy of the GNU General Public License version
18
* 2 along with this work; if not, write to the Free Software Foundation,
19
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20
*
21
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22
* or visit www.oracle.com if you need additional information or have any
23
* questions.
24
*/
25
26
package sun.rmi.log;
27
28
import java.io.*;
29
import java.lang.reflect.Constructor;
30
import java.rmi.server.RMIClassLoader;
31
import java.security.AccessController;
32
import java.security.PrivilegedAction;
33
import sun.security.action.GetBooleanAction;
34
import sun.security.action.GetPropertyAction;
35
36
/**
37
* This class is a simple implementation of a reliable Log. The
38
* client of a ReliableLog must provide a set of callbacks (via a
39
* LogHandler) that enables a ReliableLog to read and write
40
* checkpoints and log records. This implementation ensures that the
41
* current value of the data stored (via a ReliableLog) is recoverable
42
* after a system crash. <p>
43
*
44
* The secondary storage strategy is to record values in files using a
45
* representation of the caller's choosing. Two sorts of files are
46
* kept: snapshots and logs. At any instant, one snapshot is current.
47
* The log consists of a sequence of updates that have occurred since
48
* the current snapshot was taken. The current stable state is the
49
* value of the snapshot, as modified by the sequence of updates in
50
* the log. From time to time, the client of a ReliableLog instructs
51
* the package to make a new snapshot and clear the log. A ReliableLog
52
* arranges disk writes such that updates are stable (as long as the
53
* changes are force-written to disk) and atomic : no update is lost,
54
* and each update either is recorded completely in the log or not at
55
* all. Making a new snapshot is also atomic. <p>
56
*
57
* Normal use for maintaining the recoverable store is as follows: The
58
* client maintains the relevant data structure in virtual memory. As
59
* updates happen to the structure, the client informs the ReliableLog
60
* (all it "log") by calling log.update. Periodically, the client
61
* calls log.snapshot to provide the current value of the data
62
* structure. On restart, the client calls log.recover to obtain the
63
* latest snapshot and the following sequences of updates; the client
64
* applies the updates to the snapshot to obtain the state that
65
* existed before the crash. <p>
66
*
67
* The current logfile format is: <ol>
68
* <li> a format version number (two 4-octet integers, major and
69
* minor), followed by
70
* <li> a sequence of log records. Each log record contains, in
71
* order, <ol>
72
* <li> a 4-octet integer representing the length of the following log
73
* data,
74
* <li> the log data (variable length). </ol> </ol> <p>
75
*
76
* @see LogHandler
77
*
78
* @author Ann Wollrath
79
*
80
*/
81
public class ReliableLog {
82
83
public final static int PreferredMajorVersion = 0;
84
public final static int PreferredMinorVersion = 2;
85
86
// sun.rmi.log.debug=false
87
private boolean Debug = false;
88
89
private static String snapshotPrefix = "Snapshot.";
90
private static String logfilePrefix = "Logfile.";
91
private static String versionFile = "Version_Number";
92
private static String newVersionFile = "New_Version_Number";
93
private static int intBytes = 4;
94
private static long diskPageSize = 512;
95
96
private File dir; // base directory
97
private int version = 0; // current snapshot and log version
98
private String logName = null;
99
private LogFile log = null;
100
private long snapshotBytes = 0;
101
private long logBytes = 0;
102
private int logEntries = 0;
103
private long lastSnapshot = 0;
104
private long lastLog = 0;
105
//private long padBoundary = intBytes;
106
private LogHandler handler;
107
private final byte[] intBuf = new byte[4];
108
109
// format version numbers read from/written to this.log
110
private int majorFormatVersion = 0;
111
private int minorFormatVersion = 0;
112
113
114
/**
115
* Constructor for the log file. If the system property
116
* sun.rmi.log.class is non-null and the class specified by this
117
* property a) can be loaded, b) is a subclass of LogFile, and c) has a
118
* public two-arg constructor (String, String), ReliableLog uses the
119
* constructor to construct the LogFile.
120
**/
121
private static final Constructor<? extends LogFile>
122
logClassConstructor = getLogClassConstructor();
123
124
/**
125
* Creates a ReliableLog to handle checkpoints and logging in a
126
* stable storage directory.
127
*
128
* @param dirPath path to the stable storage directory
129
* @param logCl the closure object containing callbacks for logging and
130
* recovery
131
* @param pad ignored
132
* @exception IOException If a directory creation error has
133
* occurred or if initialSnapshot callback raises an exception or
134
* if an exception occurs during invocation of the handler's
135
* snapshot method or if other IOException occurs.
136
*/
137
public ReliableLog(String dirPath,
138
LogHandler handler,
139
boolean pad)
140
throws IOException
141
{
142
super();
143
this.Debug = AccessController.doPrivileged(
144
new GetBooleanAction("sun.rmi.log.debug")).booleanValue();
145
dir = new File(dirPath);
146
if (!(dir.exists() && dir.isDirectory())) {
147
// create directory
148
if (!dir.mkdir()) {
149
throw new IOException("could not create directory for log: " +
150
dirPath);
151
}
152
}
153
//padBoundary = (pad ? diskPageSize : intBytes);
154
this.handler = handler;
155
lastSnapshot = 0;
156
lastLog = 0;
157
getVersion();
158
if (version == 0) {
159
try {
160
snapshot(handler.initialSnapshot());
161
} catch (IOException e) {
162
throw e;
163
} catch (Exception e) {
164
throw new IOException("initial snapshot failed with " +
165
"exception: " + e);
166
}
167
}
168
}
169
170
/**
171
* Creates a ReliableLog to handle checkpoints and logging in a
172
* stable storage directory.
173
*
174
* @param dirPath path to the stable storage directory
175
* @param logCl the closure object containing callbacks for logging and
176
* recovery
177
* @exception IOException If a directory creation error has
178
* occurred or if initialSnapshot callback raises an exception
179
*/
180
public ReliableLog(String dirPath,
181
LogHandler handler)
182
throws IOException
183
{
184
this(dirPath, handler, false);
185
}
186
187
/* public methods */
188
189
/**
190
* Returns an object which is the value recorded in the current
191
* snapshot. This snapshot is recovered by calling the client
192
* supplied callback "recover" and then subsequently invoking
193
* the "readUpdate" callback to apply any logged updates to the state.
194
*
195
* @exception IOException If recovery fails due to serious log
196
* corruption, read update failure, or if an exception occurs
197
* during the recover callback
198
*/
199
public synchronized Object recover()
200
throws IOException
201
{
202
if (Debug)
203
System.err.println("log.debug: recover()");
204
205
if (version == 0)
206
return null;
207
208
Object snapshot;
209
String fname = versionName(snapshotPrefix);
210
File snapshotFile = new File(fname);
211
InputStream in =
212
new BufferedInputStream(new FileInputStream(snapshotFile));
213
214
if (Debug)
215
System.err.println("log.debug: recovering from " + fname);
216
217
try {
218
try {
219
snapshot = handler.recover(in);
220
221
} catch (IOException e) {
222
throw e;
223
} catch (Exception e) {
224
if (Debug)
225
System.err.println("log.debug: recovery failed: " + e);
226
throw new IOException("log recover failed with " +
227
"exception: " + e);
228
}
229
snapshotBytes = snapshotFile.length();
230
} finally {
231
in.close();
232
}
233
234
return recoverUpdates(snapshot);
235
}
236
237
/**
238
* Records this update in the log file (does not force update to disk).
239
* The update is recorded by calling the client's "writeUpdate" callback.
240
* This method must not be called until this log's recover method has
241
* been invoked (and completed).
242
*
243
* @param value the object representing the update
244
* @exception IOException If an exception occurred during a
245
* writeUpdate callback or if other I/O error has occurred.
246
*/
247
public synchronized void update(Object value) throws IOException {
248
update(value, true);
249
}
250
251
/**
252
* Records this update in the log file. The update is recorded by
253
* calling the client's writeUpdate callback. This method must not be
254
* called until this log's recover method has been invoked
255
* (and completed).
256
*
257
* @param value the object representing the update
258
* @param forceToDisk ignored; changes are always forced to disk
259
* @exception IOException If force-write to log failed or an
260
* exception occurred during the writeUpdate callback or if other
261
* I/O error occurs while updating the log.
262
*/
263
public synchronized void update(Object value, boolean forceToDisk)
264
throws IOException
265
{
266
// avoid accessing a null log field.
267
if (log == null) {
268
throw new IOException("log is inaccessible, " +
269
"it may have been corrupted or closed");
270
}
271
272
/*
273
* If the entry length field spans a sector boundary, write
274
* the high order bit of the entry length, otherwise write zero for
275
* the entry length.
276
*/
277
long entryStart = log.getFilePointer();
278
boolean spansBoundary = log.checkSpansBoundary(entryStart);
279
writeInt(log, spansBoundary? 1<<31 : 0);
280
281
/*
282
* Write update, and sync.
283
*/
284
try {
285
handler.writeUpdate(new LogOutputStream(log), value);
286
} catch (IOException e) {
287
throw e;
288
} catch (Exception e) {
289
throw (IOException)
290
new IOException("write update failed").initCause(e);
291
}
292
log.sync();
293
294
long entryEnd = log.getFilePointer();
295
int updateLen = (int) ((entryEnd - entryStart) - intBytes);
296
log.seek(entryStart);
297
298
if (spansBoundary) {
299
/*
300
* If length field spans a sector boundary, then
301
* the next two steps are required (see 4652922):
302
*
303
* 1) Write actual length with high order bit set; sync.
304
* 2) Then clear high order bit of length; sync.
305
*/
306
writeInt(log, updateLen | 1<<31);
307
log.sync();
308
309
log.seek(entryStart);
310
log.writeByte(updateLen >> 24);
311
log.sync();
312
313
} else {
314
/*
315
* Write actual length; sync.
316
*/
317
writeInt(log, updateLen);
318
log.sync();
319
}
320
321
log.seek(entryEnd);
322
logBytes = entryEnd;
323
lastLog = System.currentTimeMillis();
324
logEntries++;
325
}
326
327
/**
328
* Returns the constructor for the log file if the system property
329
* sun.rmi.log.class is non-null and the class specified by the
330
* property a) can be loaded, b) is a subclass of LogFile, and c) has a
331
* public two-arg constructor (String, String); otherwise returns null.
332
**/
333
private static Constructor<? extends LogFile>
334
getLogClassConstructor() {
335
336
String logClassName = AccessController.doPrivileged(
337
new GetPropertyAction("sun.rmi.log.class"));
338
if (logClassName != null) {
339
try {
340
ClassLoader loader =
341
AccessController.doPrivileged(
342
new PrivilegedAction<ClassLoader>() {
343
public ClassLoader run() {
344
return ClassLoader.getSystemClassLoader();
345
}
346
});
347
Class<? extends LogFile> cl =
348
loader.loadClass(logClassName).asSubclass(LogFile.class);
349
return cl.getConstructor(String.class, String.class);
350
} catch (Exception e) {
351
System.err.println("Exception occurred:");
352
e.printStackTrace();
353
}
354
}
355
return null;
356
}
357
358
/**
359
* Records this value as the current snapshot by invoking the client
360
* supplied "snapshot" callback and then empties the log.
361
*
362
* @param value the object representing the new snapshot
363
* @exception IOException If an exception occurred during the
364
* snapshot callback or if other I/O error has occurred during the
365
* snapshot process
366
*/
367
public synchronized void snapshot(Object value)
368
throws IOException
369
{
370
int oldVersion = version;
371
incrVersion();
372
373
String fname = versionName(snapshotPrefix);
374
File snapshotFile = new File(fname);
375
FileOutputStream out = new FileOutputStream(snapshotFile);
376
try {
377
try {
378
handler.snapshot(out, value);
379
} catch (IOException e) {
380
throw e;
381
} catch (Exception e) {
382
throw new IOException("snapshot failed", e);
383
}
384
lastSnapshot = System.currentTimeMillis();
385
} finally {
386
out.close();
387
snapshotBytes = snapshotFile.length();
388
}
389
390
openLogFile(true);
391
writeVersionFile(true);
392
commitToNewVersion();
393
deleteSnapshot(oldVersion);
394
deleteLogFile(oldVersion);
395
}
396
397
/**
398
* Close the stable storage directory in an orderly manner.
399
*
400
* @exception IOException If an I/O error occurs when the log is
401
* closed
402
*/
403
public synchronized void close() throws IOException {
404
if (log == null) return;
405
try {
406
log.close();
407
} finally {
408
log = null;
409
}
410
}
411
412
/**
413
* Returns the size of the snapshot file in bytes;
414
*/
415
public long snapshotSize() {
416
return snapshotBytes;
417
}
418
419
/**
420
* Returns the size of the log file in bytes;
421
*/
422
public long logSize() {
423
return logBytes;
424
}
425
426
/* private methods */
427
428
/**
429
* Write an int value in single write operation. This method
430
* assumes that the caller is synchronized on the log file.
431
*
432
* @param out output stream
433
* @param val int value
434
* @throws IOException if any other I/O error occurs
435
*/
436
private void writeInt(DataOutput out, int val)
437
throws IOException
438
{
439
intBuf[0] = (byte) (val >> 24);
440
intBuf[1] = (byte) (val >> 16);
441
intBuf[2] = (byte) (val >> 8);
442
intBuf[3] = (byte) val;
443
out.write(intBuf);
444
}
445
446
/**
447
* Generates a filename prepended with the stable storage directory path.
448
*
449
* @param name the leaf name of the file
450
*/
451
private String fName(String name) {
452
return dir.getPath() + File.separator + name;
453
}
454
455
/**
456
* Generates a version 0 filename prepended with the stable storage
457
* directory path
458
*
459
* @param name version file name
460
*/
461
private String versionName(String name) {
462
return versionName(name, 0);
463
}
464
465
/**
466
* Generates a version filename prepended with the stable storage
467
* directory path with the version number as a suffix.
468
*
469
* @param name version file name
470
* @thisversion a version number
471
*/
472
private String versionName(String prefix, int ver) {
473
ver = (ver == 0) ? version : ver;
474
return fName(prefix) + String.valueOf(ver);
475
}
476
477
/**
478
* Increments the directory version number.
479
*/
480
private void incrVersion() {
481
do { version++; } while (version==0);
482
}
483
484
/**
485
* Delete a file.
486
*
487
* @param name the name of the file
488
* @exception IOException If new version file couldn't be removed
489
*/
490
private void deleteFile(String name) throws IOException {
491
492
File f = new File(name);
493
if (!f.delete())
494
throw new IOException("couldn't remove file: " + name);
495
}
496
497
/**
498
* Removes the new version number file.
499
*
500
* @exception IOException If an I/O error has occurred.
501
*/
502
private void deleteNewVersionFile() throws IOException {
503
deleteFile(fName(newVersionFile));
504
}
505
506
/**
507
* Removes the snapshot file.
508
*
509
* @param ver the version to remove
510
* @exception IOException If an I/O error has occurred.
511
*/
512
private void deleteSnapshot(int ver) throws IOException {
513
if (ver == 0) return;
514
deleteFile(versionName(snapshotPrefix, ver));
515
}
516
517
/**
518
* Removes the log file.
519
*
520
* @param ver the version to remove
521
* @exception IOException If an I/O error has occurred.
522
*/
523
private void deleteLogFile(int ver) throws IOException {
524
if (ver == 0) return;
525
deleteFile(versionName(logfilePrefix, ver));
526
}
527
528
/**
529
* Opens the log file in read/write mode. If file does not exist, it is
530
* created.
531
*
532
* @param truncate if true and file exists, file is truncated to zero
533
* length
534
* @exception IOException If an I/O error has occurred.
535
*/
536
private void openLogFile(boolean truncate) throws IOException {
537
try {
538
close();
539
} catch (IOException e) { /* assume this is okay */
540
}
541
542
logName = versionName(logfilePrefix);
543
544
try {
545
log = (logClassConstructor == null ?
546
new LogFile(logName, "rw") :
547
logClassConstructor.newInstance(logName, "rw"));
548
} catch (Exception e) {
549
throw (IOException) new IOException(
550
"unable to construct LogFile instance").initCause(e);
551
}
552
553
if (truncate) {
554
initializeLogFile();
555
}
556
}
557
558
/**
559
* Creates a new log file, truncated and initialized with the format
560
* version number preferred by this implementation.
561
* <p>Environment: inited, synchronized
562
* <p>Precondition: valid: log, log contains nothing useful
563
* <p>Postcondition: if successful, log is initialised with the format
564
* version number (Preferred{Major,Minor}Version), and logBytes is
565
* set to the resulting size of the updatelog, and logEntries is set to
566
* zero. Otherwise, log is in an indeterminate state, and logBytes
567
* is unchanged, and logEntries is unchanged.
568
*
569
* @exception IOException If an I/O error has occurred.
570
*/
571
private void initializeLogFile()
572
throws IOException
573
{
574
log.setLength(0);
575
majorFormatVersion = PreferredMajorVersion;
576
writeInt(log, PreferredMajorVersion);
577
minorFormatVersion = PreferredMinorVersion;
578
writeInt(log, PreferredMinorVersion);
579
logBytes = intBytes * 2;
580
logEntries = 0;
581
}
582
583
584
/**
585
* Writes out version number to file.
586
*
587
* @param newVersion if true, writes to a new version file
588
* @exception IOException If an I/O error has occurred.
589
*/
590
private void writeVersionFile(boolean newVersion) throws IOException {
591
String name;
592
if (newVersion) {
593
name = newVersionFile;
594
} else {
595
name = versionFile;
596
}
597
try (FileOutputStream fos = new FileOutputStream(fName(name));
598
DataOutputStream out = new DataOutputStream(fos)) {
599
writeInt(out, version);
600
}
601
}
602
603
/**
604
* Creates the initial version file
605
*
606
* @exception IOException If an I/O error has occurred.
607
*/
608
private void createFirstVersion() throws IOException {
609
version = 0;
610
writeVersionFile(false);
611
}
612
613
/**
614
* Commits (atomically) the new version.
615
*
616
* @exception IOException If an I/O error has occurred.
617
*/
618
private void commitToNewVersion() throws IOException {
619
writeVersionFile(false);
620
deleteNewVersionFile();
621
}
622
623
/**
624
* Reads version number from a file.
625
*
626
* @param name the name of the version file
627
* @return the version
628
* @exception IOException If an I/O error has occurred.
629
*/
630
private int readVersion(String name) throws IOException {
631
try (DataInputStream in = new DataInputStream
632
(new FileInputStream(name))) {
633
return in.readInt();
634
}
635
}
636
637
/**
638
* Sets the version. If version file does not exist, the initial
639
* version file is created.
640
*
641
* @exception IOException If an I/O error has occurred.
642
*/
643
private void getVersion() throws IOException {
644
try {
645
version = readVersion(fName(newVersionFile));
646
commitToNewVersion();
647
} catch (IOException e) {
648
try {
649
deleteNewVersionFile();
650
}
651
catch (IOException ex) {
652
}
653
654
try {
655
version = readVersion(fName(versionFile));
656
}
657
catch (IOException ex) {
658
createFirstVersion();
659
}
660
}
661
}
662
663
/**
664
* Applies outstanding updates to the snapshot.
665
*
666
* @param state the most recent snapshot
667
* @exception IOException If serious log corruption is detected or
668
* if an exception occurred during a readUpdate callback or if
669
* other I/O error has occurred.
670
* @return the resulting state of the object after all updates
671
*/
672
private Object recoverUpdates(Object state)
673
throws IOException
674
{
675
logBytes = 0;
676
logEntries = 0;
677
678
if (version == 0) return state;
679
680
String fname = versionName(logfilePrefix);
681
InputStream in =
682
new BufferedInputStream(new FileInputStream(fname));
683
DataInputStream dataIn = new DataInputStream(in);
684
685
if (Debug)
686
System.err.println("log.debug: reading updates from " + fname);
687
688
try {
689
majorFormatVersion = dataIn.readInt(); logBytes += intBytes;
690
minorFormatVersion = dataIn.readInt(); logBytes += intBytes;
691
} catch (EOFException e) {
692
/* This is a log which was corrupted and/or cleared (by
693
* fsck or equivalent). This is not an error.
694
*/
695
openLogFile(true); // create and truncate
696
in = null;
697
}
698
/* A new major version number is a catastrophe (it means
699
* that the file format is incompatible with older
700
* clients, and we'll only be breaking things by trying to
701
* use the log). A new minor version is no big deal for
702
* upward compatibility.
703
*/
704
if (majorFormatVersion != PreferredMajorVersion) {
705
if (Debug) {
706
System.err.println("log.debug: major version mismatch: " +
707
majorFormatVersion + "." + minorFormatVersion);
708
}
709
throw new IOException("Log file " + logName + " has a " +
710
"version " + majorFormatVersion +
711
"." + minorFormatVersion +
712
" format, and this implementation " +
713
" understands only version " +
714
PreferredMajorVersion + "." +
715
PreferredMinorVersion);
716
}
717
718
try {
719
while (in != null) {
720
int updateLen = 0;
721
722
try {
723
updateLen = dataIn.readInt();
724
} catch (EOFException e) {
725
if (Debug)
726
System.err.println("log.debug: log was sync'd cleanly");
727
break;
728
}
729
if (updateLen <= 0) {/* crashed while writing last log entry */
730
if (Debug) {
731
System.err.println(
732
"log.debug: last update incomplete, " +
733
"updateLen = 0x" +
734
Integer.toHexString(updateLen));
735
}
736
break;
737
}
738
739
// this is a fragile use of available() which relies on the
740
// twin facts that BufferedInputStream correctly consults
741
// the underlying stream, and that FileInputStream returns
742
// the number of bytes remaining in the file (via FIONREAD).
743
if (in.available() < updateLen) {
744
/* corrupted record at end of log (can happen since we
745
* do only one fsync)
746
*/
747
if (Debug)
748
System.err.println("log.debug: log was truncated");
749
break;
750
}
751
752
if (Debug)
753
System.err.println("log.debug: rdUpdate size " + updateLen);
754
try {
755
state = handler.readUpdate(new LogInputStream(in, updateLen),
756
state);
757
} catch (IOException e) {
758
throw e;
759
} catch (Exception e) {
760
e.printStackTrace();
761
throw new IOException("read update failed with " +
762
"exception: " + e);
763
}
764
logBytes += (intBytes + updateLen);
765
logEntries++;
766
} /* while */
767
} finally {
768
if (in != null)
769
in.close();
770
}
771
772
if (Debug)
773
System.err.println("log.debug: recovered updates: " + logEntries);
774
775
/* reopen log file at end */
776
openLogFile(false);
777
778
// avoid accessing a null log field
779
if (log == null) {
780
throw new IOException("rmid's log is inaccessible, " +
781
"it may have been corrupted or closed");
782
}
783
784
log.seek(logBytes);
785
log.setLength(logBytes);
786
787
return state;
788
}
789
790
/**
791
* ReliableLog's log file implementation. This implementation
792
* is subclassable for testing purposes.
793
*/
794
public static class LogFile extends RandomAccessFile {
795
796
private final FileDescriptor fd;
797
798
/**
799
* Constructs a LogFile and initializes the file descriptor.
800
**/
801
public LogFile(String name, String mode)
802
throws FileNotFoundException, IOException
803
{
804
super(name, mode);
805
this.fd = getFD();
806
}
807
808
/**
809
* Invokes sync on the file descriptor for this log file.
810
*/
811
protected void sync() throws IOException {
812
fd.sync();
813
}
814
815
/**
816
* Returns true if writing 4 bytes starting at the specified file
817
* position, would span a 512 byte sector boundary; otherwise returns
818
* false.
819
**/
820
protected boolean checkSpansBoundary(long fp) {
821
return fp % 512 > 508;
822
}
823
}
824
}
825
826