-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathinode.js
157 lines (132 loc) · 5.03 KB
/
inode.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
"use strict";
/* globals require, module, Buffer */
var config = require("../config.js");
var log = require("../jlog.js");
var utils = require("./utils.js");
var file_types = require("./file-types.js");
// global to keep track of storage location rotation
var next_storage_location = 0;
var Inode = {
init: function(url){
this.input_buffer = new Buffer("");
this.block_size = config.BLOCK_SIZE;
this.file_metadata = {};
this.file_metadata.url = url;
this.file_metadata.created = (new Date()).getTime();
this.file_metadata.version = 0;
this.file_metadata.private = false;
this.file_metadata.encrypted = false;
this.file_metadata.fingerprint = null;
this.file_metadata.access_key = null;
this.file_metadata.content_type = "application/octet-stream";
this.file_metadata.file_size = 0;
this.file_metadata.block_size = this.block_size;
this.file_metadata.blocks_replicated = 0;
this.file_metadata.inode_replicated = 0;
this.file_metadata.blocks = [];
// create fingerprint to uniquely identify this file
this.file_metadata.fingerprint = utils.sha1_to_hex(this.file_metadata.url);
// use fingerprint as default key
this.file_metadata.access_key = this.file_metadata.fingerprint;
// experimental executable support (jsfsx)
this.file_metadata.executable = false;
},
write: function(chunk, req, callback){
this.input_buffer = new Buffer.concat([this.input_buffer, chunk]);
if (this.input_buffer.length > this.block_size) {
req.pause();
this.process_buffer(false, function(result){
req.resume();
callback(result);
});
} else {
callback(true);
}
},
close: function(callback){
var self = this;
log.message(0, "flushing remaining buffer");
// update original file size
self.file_metadata.file_size = self.file_metadata.file_size + self.input_buffer.length;
self.process_buffer(true, function(result){
if(result){
// write inode to disk
utils.save_inode(self.file_metadata, callback);
}
});
},
process_buffer: function(flush, callback){
var self = this;
var total = flush ? 0 : self.block_size;
this.store_block(!flush, function(err/*, result*/){
if (err) {
log.message(log.DEBUG, "process_buffer result: " + err);
return callback(false);
}
if (self.input_buffer.length > total) {
self.process_buffer(flush, callback);
} else {
callback(true);
}
});
},
store_block: function(update_file_size, callback){
var self = this;
var chunk_size = this.block_size;
// grab the next block
var block = this.input_buffer.slice(0, chunk_size);
if(this.file_metadata.blocks.length === 0){
// grok known file types
var analysis_result = file_types.analyze(block);
log.message(log.INFO, "block analysis result: " + JSON.stringify(analysis_result));
// if we found out anything useful, annotate the object's metadata
this.file_metadata.media_type = analysis_result.type;
if(analysis_result.type != "unknown"){
this.file_metadata.media_size = analysis_result.size;
this.file_metadata.media_channels = analysis_result.channels;
this.file_metadata.media_bitrate = analysis_result.bitrate;
this.file_metadata.media_resolution = analysis_result.resolution;
this.file_metadata.media_duration = analysis_result.duration;
}
if (analysis_result.type === "wave") {
chunk_size = utils.wave_audio_offset(block, analysis_result)
block = block.slice(0, chunk_size);
}
}
// if encryption is set, encrypt using the hash above
if(this.file_metadata.encrypted && this.file_metadata.access_key){
log.message(log.INFO, "encrypting block");
block = utils.encrypt(block, this.file_metadata.access_key);
} else {
// if even one block can't be encrypted, say so and stop trying
this.file_metadata.encrypted = false;
}
// store the block
var block_object = {};
// generate a hash of the block to use as a handle/filename
block_object.block_hash = utils.sha1_to_hex(block);
utils.commit_block_to_disk(block, block_object, next_storage_location, function(err, result){
if (err) {
return callback(err);
}
// increment (or reset) storage location (striping)
next_storage_location++;
if(next_storage_location === config.STORAGE_LOCATIONS.length){
next_storage_location = 0;
}
// update inode
self.file_metadata.blocks.push(result);
// update original file size
// we need to update filesize here due to truncation at the front,
// but need the check to avoid double setting during flush
// is there a better way?
if (update_file_size) {
self.file_metadata.file_size = self.file_metadata.file_size + chunk_size;
}
// advance buffer
self.input_buffer = self.input_buffer.slice(chunk_size);
return callback(null, result);
});
}
};
module.exports = Inode;