node.jsrenamedelete-filefschallenge-response

Strange behaviour with fs.rename : file deletion (node.js)


I wanted to write a custom script that would rename all of the files in a directory that contains a dot in the filename, recursively. Like : mv 'foo.bar.avi' 'foo bar.avi'

It also converts directories names like 'a.b.c/x.y.z.mp4' becomes 'a b c/x y.mp4' and removes all underscores _.

The result ended in deleting all of the files in this directory, leaving only some empty directories, without any notice or error, making me lose definitely around 300 Gb of unbackuped data.

There is the wonderful code that allowed this feat :

var fs = require('fs');
var path = require('path');
var util = require('util');
var fse = require('fs-extra')
var async = require('async');
var walk = function(dir, done) {
  var results = [];
  fs.readdir(dir, function(err, list) {
    if (err) return done(err);
    var pending = list.length;
    if (!pending) return done(null, results);
    list.forEach(function(file) {
      file = path.resolve(dir, file);
      fs.stat(file, function(err, stat) {
        if (stat && stat.isDirectory()) {
          walk(file, function(err, res) {
            results = results.concat(res);
            if (!--pending) done(null, results);
          });
        } else {
          if (file.indexOf('/.') < 0) {
            results.push(file);
          }
          if (!--pending) done(null, results);
        }
      });
    });
  });
};
var rename = function(err, filename) {
    if (util.isArray(filename)) {
      for (var i = 0; i < filename.length; i++) {
        rename(err, filename[i]);
      }
      return;
    }
    fs.stat(filename, function(err, stat) {
      if (!stat.isDirectory()) {
        filename2 = filename.split('.');
        var extension = filename2.pop();
        filename2 = filename2.join(' ').split('_').join(' ') + '.' + extension;
        var directory = filename2.split('/');
        directory.pop();
        directory = directory.join('/');
        async.series([
          function (callback) {
            fse.mkdirs(directory, function (err) {
              callback(err, 'fs.mkdirs');
            });
          },
          function(callback) {
            fs.rename(filename, filename2, function (err) {
              callback(err, 'fs.rename');
            });
          }
        ],
        function (err, results) {
          if (err) {
            console.log(err);
          }
          console.log('All files have been renamed');
        });
      }
    });
}
walk('/Volumes/My Passport/Movies/', function(err, results) {
    rename('', results);
});

It's too late to do anything, I have already mourning this loss.

I just would like to understand how this fs.rename could delete the files.

I can not believe I did it. I am not even mad, it's amazing. My script deleted 300gb of data without even one single instruction about deleting. I accepted my faith.

I suspect that fs.rename is buggy on external drives.

Thank you.


Solution

  • So, that is a pity to say but the problem was that you had forgotten var when declaring filename2. Thus, filename2 became a global variable. fs.rename was getting the same value every time. The solution would be either declare filename2 as var filename2 or get rid of async.series.