1 /*
2 * Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *
8 * - Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 *
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * - Neither the name of Oracle nor the names of its
16 * contributors may be used to endorse or promote products derived
17 * from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
20 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
480 }
481 if (!hasCopyAttrs)
482 u.mtime = u.atime= u.ctime = System.currentTimeMillis();
483 update(u);
484 if (deletesrc)
485 updateDelete(eSrc);
486 } finally {
487 endWrite();
488 }
489 }
490
491 // Returns an output stream for writing the contents into the specified
492 // entry.
493 OutputStream newOutputStream(byte[] path, OpenOption... options)
494 throws IOException
495 {
496 checkWritable();
497 boolean hasCreateNew = false;
498 boolean hasCreate = false;
499 boolean hasAppend = false;
500 for (OpenOption opt: options) {
501 if (opt == READ)
502 throw new IllegalArgumentException("READ not allowed");
503 if (opt == CREATE_NEW)
504 hasCreateNew = true;
505 if (opt == CREATE)
506 hasCreate = true;
507 if (opt == APPEND)
508 hasAppend = true;
509 }
510 beginRead(); // only need a readlock, the "update()" will
511 try { // try to obtain a writelock when the os is
512 ensureOpen(); // being closed.
513 Entry e = getEntry0(path);
514 if (e != null) {
515 if (e.isDir() || hasCreateNew)
516 throw new FileAlreadyExistsException(getString(path));
517 if (hasAppend) {
518 InputStream is = getInputStream(e);
519 OutputStream os = getOutputStream(new Entry(e, Entry.NEW));
520 copyStream(is, os);
521 is.close();
522 return os;
523 }
524 return getOutputStream(new Entry(e, Entry.NEW));
525 } else {
526 if (!hasCreate && !hasCreateNew)
527 throw new NoSuchFileException(getString(path));
528 checkParents(path);
529 return getOutputStream(new Entry(path, Entry.NEW));
541 ensureOpen();
542 Entry e = getEntry0(path);
543 if (e == null)
544 throw new NoSuchFileException(getString(path));
545 if (e.isDir())
546 throw new FileSystemException(getString(path), "is a directory", null);
547 return getInputStream(e);
548 } finally {
549 endRead();
550 }
551 }
552
553 private void checkOptions(Set<? extends OpenOption> options) {
554 // check for options of null type and option is an intance of StandardOpenOption
555 for (OpenOption option : options) {
556 if (option == null)
557 throw new NullPointerException();
558 if (!(option instanceof StandardOpenOption))
559 throw new IllegalArgumentException();
560 }
561 }
562
563 // Returns a Writable/ReadByteChannel for now. Might consdier to use
564 // newFileChannel() instead, which dump the entry data into a regular
565 // file on the default file system and create a FileChannel on top of
566 // it.
567 SeekableByteChannel newByteChannel(byte[] path,
568 Set<? extends OpenOption> options,
569 FileAttribute<?>... attrs)
570 throws IOException
571 {
572 checkOptions(options);
573 if (options.contains(StandardOpenOption.WRITE) ||
574 options.contains(StandardOpenOption.APPEND)) {
575 checkWritable();
576 beginRead();
577 try {
578 final WritableByteChannel wbc = Channels.newChannel(
579 newOutputStream(path, options.toArray(new OpenOption[0])));
580 long leftover = 0;
688 // Returns a FileChannel of the specified entry.
689 //
690 // This implementation creates a temporary file on the default file system,
691 // copy the entry data into it if the entry exists, and then create a
692 // FileChannel on top of it.
693 FileChannel newFileChannel(byte[] path,
694 Set<? extends OpenOption> options,
695 FileAttribute<?>... attrs)
696 throws IOException
697 {
698 checkOptions(options);
699 final boolean forWrite = (options.contains(StandardOpenOption.WRITE) ||
700 options.contains(StandardOpenOption.APPEND));
701 beginRead();
702 try {
703 ensureOpen();
704 Entry e = getEntry0(path);
705 if (forWrite) {
706 checkWritable();
707 if (e == null) {
708 if (!options.contains(StandardOpenOption.CREATE_NEW))
709 throw new NoSuchFileException(getString(path));
710 } else {
711 if (options.contains(StandardOpenOption.CREATE_NEW))
712 throw new FileAlreadyExistsException(getString(path));
713 if (e.isDir())
714 throw new FileAlreadyExistsException("directory <"
715 + getString(path) + "> exists");
716 }
717 options.remove(StandardOpenOption.CREATE_NEW); // for tmpfile
718 } else if (e == null || e.isDir()) {
719 throw new NoSuchFileException(getString(path));
720 }
721
722 final boolean isFCH = (e != null && e.type == Entry.FILECH);
723 final Path tmpfile = isFCH ? e.file : getTempPathForEntry(path);
724 final FileChannel fch = tmpfile.getFileSystem()
725 .provider()
726 .newFileChannel(tmpfile, options, attrs);
727 final Entry u = isFCH ? e : new Entry(path, tmpfile, Entry.FILECH);
728 if (forWrite) {
729 u.flag = FLAG_DATADESCR;
730 u.method = METHOD_DEFLATED;
731 }
732 // is there a better way to hook into the FileChannel's close method?
733 return new FileChannel() {
734 public int write(ByteBuffer src) throws IOException {
735 return fch.write(src);
736 }
|
1 /*
2 * Copyright (c) 2009, 2019, Oracle and/or its affiliates. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *
8 * - Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 *
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * - Neither the name of Oracle nor the names of its
16 * contributors may be used to endorse or promote products derived
17 * from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
20 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
480 }
481 if (!hasCopyAttrs)
482 u.mtime = u.atime= u.ctime = System.currentTimeMillis();
483 update(u);
484 if (deletesrc)
485 updateDelete(eSrc);
486 } finally {
487 endWrite();
488 }
489 }
490
491 // Returns an output stream for writing the contents into the specified
492 // entry.
493 OutputStream newOutputStream(byte[] path, OpenOption... options)
494 throws IOException
495 {
496 checkWritable();
497 boolean hasCreateNew = false;
498 boolean hasCreate = false;
499 boolean hasAppend = false;
500 boolean hasTruncate = false;
501 for (OpenOption opt: options) {
502 if (opt == READ)
503 throw new IllegalArgumentException("READ not allowed");
504 if (opt == CREATE_NEW)
505 hasCreateNew = true;
506 if (opt == CREATE)
507 hasCreate = true;
508 if (opt == APPEND)
509 hasAppend = true;
510 if (opt == TRUNCATE_EXISTING)
511 hasTruncate = true;
512 }
513 if (hasAppend && hasTruncate)
514 throw new IllegalArgumentException("APPEND + TRUNCATE_EXISTING not allowed");
515 beginRead(); // only need a readlock, the "update()" will
516 try { // try to obtain a writelock when the os is
517 ensureOpen(); // being closed.
518 Entry e = getEntry0(path);
519 if (e != null) {
520 if (e.isDir() || hasCreateNew)
521 throw new FileAlreadyExistsException(getString(path));
522 if (hasAppend) {
523 InputStream is = getInputStream(e);
524 OutputStream os = getOutputStream(new Entry(e, Entry.NEW));
525 copyStream(is, os);
526 is.close();
527 return os;
528 }
529 return getOutputStream(new Entry(e, Entry.NEW));
530 } else {
531 if (!hasCreate && !hasCreateNew)
532 throw new NoSuchFileException(getString(path));
533 checkParents(path);
534 return getOutputStream(new Entry(path, Entry.NEW));
546 ensureOpen();
547 Entry e = getEntry0(path);
548 if (e == null)
549 throw new NoSuchFileException(getString(path));
550 if (e.isDir())
551 throw new FileSystemException(getString(path), "is a directory", null);
552 return getInputStream(e);
553 } finally {
554 endRead();
555 }
556 }
557
558 private void checkOptions(Set<? extends OpenOption> options) {
559 // check for options of null type and option is an intance of StandardOpenOption
560 for (OpenOption option : options) {
561 if (option == null)
562 throw new NullPointerException();
563 if (!(option instanceof StandardOpenOption))
564 throw new IllegalArgumentException();
565 }
566 if (options.contains(APPEND) && options.contains(TRUNCATE_EXISTING))
567 throw new IllegalArgumentException("APPEND + TRUNCATE_EXISTING not allowed");
568 }
569
570 // Returns a Writable/ReadByteChannel for now. Might consdier to use
571 // newFileChannel() instead, which dump the entry data into a regular
572 // file on the default file system and create a FileChannel on top of
573 // it.
574 SeekableByteChannel newByteChannel(byte[] path,
575 Set<? extends OpenOption> options,
576 FileAttribute<?>... attrs)
577 throws IOException
578 {
579 checkOptions(options);
580 if (options.contains(StandardOpenOption.WRITE) ||
581 options.contains(StandardOpenOption.APPEND)) {
582 checkWritable();
583 beginRead();
584 try {
585 final WritableByteChannel wbc = Channels.newChannel(
586 newOutputStream(path, options.toArray(new OpenOption[0])));
587 long leftover = 0;
695 // Returns a FileChannel of the specified entry.
696 //
697 // This implementation creates a temporary file on the default file system,
698 // copy the entry data into it if the entry exists, and then create a
699 // FileChannel on top of it.
700 FileChannel newFileChannel(byte[] path,
701 Set<? extends OpenOption> options,
702 FileAttribute<?>... attrs)
703 throws IOException
704 {
705 checkOptions(options);
706 final boolean forWrite = (options.contains(StandardOpenOption.WRITE) ||
707 options.contains(StandardOpenOption.APPEND));
708 beginRead();
709 try {
710 ensureOpen();
711 Entry e = getEntry0(path);
712 if (forWrite) {
713 checkWritable();
714 if (e == null) {
715 if (!options.contains(StandardOpenOption.CREATE) &&
716 !options.contains(StandardOpenOption.CREATE_NEW)) {
717 throw new NoSuchFileException(getString(path));
718 }
719 } else {
720 if (options.contains(StandardOpenOption.CREATE_NEW)) {
721 throw new FileAlreadyExistsException(getString(path));
722 }
723 if (e.isDir())
724 throw new FileAlreadyExistsException("directory <"
725 + getString(path) + "> exists");
726 }
727 options = new HashSet<>(options);
728 options.remove(StandardOpenOption.CREATE_NEW); // for tmpfile
729 } else if (e == null || e.isDir()) {
730 throw new NoSuchFileException(getString(path));
731 }
732
733 final boolean isFCH = (e != null && e.type == Entry.FILECH);
734 final Path tmpfile = isFCH ? e.file : getTempPathForEntry(path);
735 final FileChannel fch = tmpfile.getFileSystem()
736 .provider()
737 .newFileChannel(tmpfile, options, attrs);
738 final Entry u = isFCH ? e : new Entry(path, tmpfile, Entry.FILECH);
739 if (forWrite) {
740 u.flag = FLAG_DATADESCR;
741 u.method = METHOD_DEFLATED;
742 }
743 // is there a better way to hook into the FileChannel's close method?
744 return new FileChannel() {
745 public int write(ByteBuffer src) throws IOException {
746 return fch.write(src);
747 }
|