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