Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Copying fifos hangs. #727

Open
wants to merge 27 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
e17d624
Don't attempt to copy fifos.
bruce-one May 31, 2017
52c412f
Avoid fifos via symlinks.
bruce-one May 31, 2017
e4a911d
Better replicate cp during direct fifo copies.
bruce-one May 31, 2017
eaaa40a
Bugfix: don't break offset during copies.
bruce-one May 31, 2017
476d4b4
Test fix, use printf for osx not echo.
bruce-one Jun 1, 2017
c71edc2
Check exec exit code to fix tests on Windows.
bruce-one Jun 2, 2017
d469212
Skip fifo tests on Windows.
bruce-one Jun 2, 2017
5689dc0
Typo.
bruce-one Jun 2, 2017
777fe04
Shell out to create fifos during recursive copy.
bruce-one Jun 19, 2017
8fa217b
Handle "recursive" flag for special non-files.
bruce-one Jun 19, 2017
18c513e
Cleanup.
bruce-one Jun 19, 2017
be93375
Error messages.
bruce-one Jun 19, 2017
599a2c5
mknod fails without root.
bruce-one Jun 19, 2017
2d3af53
Linting.
bruce-one Jun 19, 2017
d942f3f
Permissions on special files.
bruce-one Jun 19, 2017
fad43aa
Logging.
bruce-one Jun 19, 2017
e6760bb
Lint.
bruce-one Jun 19, 2017
045c290
chown and chmod special files.
bruce-one Jun 19, 2017
40518f7
Use execFileSync for safety and simplicity.
bruce-one Jun 20, 2017
c69ddc3
Use mkfifo for fifo creation due for OSX compat.
bruce-one Jun 20, 2017
b2932cf
Replace unix specific code with warning.
bruce-one Jun 21, 2017
6a4a6a0
Log that special files were skipped.
bruce-one Jun 22, 2017
f7957bf
Revert "Log that special files were skipped."
bruce-one Jun 22, 2017
a4e5411
Doc.
bruce-one Jun 22, 2017
117894c
Treat special files the same whether recursive or not.
bruce-one Jun 22, 2017
48a0198
Doc.
bruce-one Jun 22, 2017
0d70322
Build doc.
bruce-one Jun 22, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 3 additions & 1 deletion README.md
Expand Up @@ -191,7 +191,9 @@ cp('-Rf', '/tmp/*', '/usr/local/*', '/home/tmp');
cp('-Rf', ['/tmp/*', '/usr/local/*'], '/home/tmp'); // same as above
```

Copies files.
Copies files. Natively, this command will skip special unix files like
fifos. To add support for special unix cases
[see this issue](https://github.com/shelljs/shelljs/issues/748) (WIP).


### pushd([options,] [dir | '-N' | '+N'])
Expand Down
38 changes: 27 additions & 11 deletions src/cp.js
Expand Up @@ -34,16 +34,30 @@ function copyFileSync(srcFile, destFile, options) {
// If we're here, destFile probably doesn't exist, so just do a normal copy
}

if (fs.lstatSync(srcFile).isSymbolicLink() && !options.followsymlink) {
try {
fs.lstatSync(destFile);
common.unlinkSync(destFile); // re-link it
} catch (e) {
// it doesn't exist, so no work needs to be done
}
var srcFileStat = fs.lstatSync(srcFile);

var symlinkFull = fs.readlinkSync(srcFile);
fs.symlinkSync(symlinkFull, destFile, isWindows ? 'junction' : null);
if (srcFileStat.isSymbolicLink()) {
if (options.followsymlink) {
// recurse to copy the target of the symlink to the dest to ensure we handle complex scenarios
return copyFileSync(fs.realpathSync(srcFile), destFile, options);
} else {
try {
fs.lstatSync(destFile);
common.unlinkSync(destFile); // re-link it
} catch (e) {
// it doesn't exist, so no work needs to be done
}

var symlinkFull = fs.readlinkSync(srcFile);
fs.symlinkSync(symlinkFull, destFile, isWindows ? 'junction' : null);
}
// skip unix-like files. Anticipate adding support via a plugin.
} else if (srcFileStat.isFIFO() || srcFileStat.isCharacterDevice() || srcFileStat.isBlockDevice()) {
var type = 'unknown file';
if (srcFileStat.isFIFO()) type = 'fifo';
if (srcFileStat.isCharacterDevice()) type = 'block device';
if (srcFileStat.isBlockDevice()) type = 'character device';
common.error('copyFileSync: ' + type + ' is not supported (' + srcFile + ')', { continue: true });
} else {
var buf = common.buffer();
var bufLength = buf.length;
Expand All @@ -67,7 +81,7 @@ function copyFileSync(srcFile, destFile, options) {
}

while (bytesRead === bufLength) {
bytesRead = fs.readSync(fdr, buf, 0, bufLength, pos);
bytesRead = fs.readSync(fdr, buf, 0, bufLength, pos || -1);
fs.writeSync(fdw, buf, 0, bytesRead);
pos += bytesRead;
}
Expand Down Expand Up @@ -204,7 +218,9 @@ function cpcheckcycle(sourceDir, srcFile) {
//@ cp('-Rf', ['/tmp/*', '/usr/local/*'], '/home/tmp'); // same as above
//@ ```
//@
//@ Copies files.
//@ Copies files. Natively, this command will skip special unix files like
//@ fifos. To add support for special unix cases
//@ [see this issue](https://github.com/shelljs/shelljs/issues/748) (WIP).
function _cp(options, sources, dest) {
// If we're missing -R, it actually implies -L (unless -P is explicit)
if (options.followsymlink) {
Expand Down
50 changes: 50 additions & 0 deletions test/cp.js
Expand Up @@ -7,6 +7,7 @@ import utils from './utils/utils';

const oldMaxDepth = shell.config.maxdepth;
const CWD = process.cwd();
const isRoot = process.getuid && process.getuid() === 0;

test.beforeEach(t => {
t.context.tmp = utils.getTempDir();
Expand Down Expand Up @@ -755,3 +756,52 @@ test('should not overwrite recently created files (not give error no-force mode)
// Ensure First file is copied
t.is(shell.cat(`${t.context.tmp}/file1`).toString(), 'test1');
});

test('should warn about fifos', t => {
if (process.platform !== 'win32') { // fs.exists doesn't support fifos on windows
shell.exec(`mkfifo ${t.context.tmp}/fifo`);
shell.exec(`printf test1 > ${t.context.tmp}/fifo`, { async: true });
t.truthy(fs.existsSync(`${t.context.tmp}/fifo`));
const result = shell.cp(`${t.context.tmp}/fifo`, `${t.context.tmp}/newFifo`);
t.truthy(shell.error());
t.is(result.code, 1);
t.falsy(fs.existsSync(`${t.context.tmp}/newFifo`));
}
});

test('should warn about fifos via symlinks', t => {
if (process.platform !== 'win32') { // fs.exists doesn't support fifos on windows
shell.exec(`mkfifo ${t.context.tmp}/fifo`);
shell.exec(`printf test1 > ${t.context.tmp}/fifo`, { async: true });
shell.pushd(t.context.tmp);
fs.symlinkSync('fifo', 'symFifo');
shell.popd();
t.truthy(fs.existsSync(`${t.context.tmp}/symFifo`));
const result = shell.cp(`${t.context.tmp}/symFifo`, `${t.context.tmp}/newFifo`);
t.truthy(shell.error());
t.is(result.code, 1);
t.falsy(fs.existsSync(`${t.context.tmp}/newFifo`));
}
});

test('should warn about character devices', t => {
if (process.platform !== 'win32' && isRoot) {
shell.exec(`mknod ${t.context.tmp}/zero c 1 5`);
t.truthy(fs.existsSync(`${t.context.tmp}/zero`));
const result = shell.cp(`${t.context.tmp}/zero`, `${t.context.tmp}/newZero`);
t.truthy(shell.error());
t.is(result.code, 1);
t.falsy(fs.existsSync(`${t.context.tmp}/newZero`));
}
});

test('should warn about block devices', t => {
if (process.platform !== 'win32' && isRoot) {
shell.exec(`mknod ${t.context.tmp}/sda1 b 8 1`);
t.truthy(fs.existsSync(`${t.context.tmp}/sda1`));
const result = shell.cp(`${t.context.tmp}/sda1`, `${t.context.tmp}/newSda1`);
t.truthy(shell.error());
t.is(result.code, 1);
t.falsy(fs.existsSync(`${t.context.tmp}/newSda1`));
}
});