2 * Copyright (c) 2007, 2008, 2009, 2010, 2011 Zmanda, Inc. All Rights Reserved.
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 2 as published
6 * by the Free Software Foundation.
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 * Contact information: Zmanda Inc., 465 S. Mathilda Ave., Suite 300
18 * Sunnyvale, CA 94085, USA, or: http://www.zmanda.com
22 #include <string.h> /* memset() */
27 #include "vfs-device.h"
29 /* This regex will match all VfsDevice files in a directory. We use it
30 for cleanup and verification. Note that this regex does NOT match
32 #define VFS_DEVICE_FILE_REGEX "^[0-9]+[\\.-]"
34 /* The name of the volume lockfile. Should be the same as that
35 generated by lockfile_name(0). */
36 #define VOLUME_LOCKFILE_NAME "00000-lock"
38 #define VFS_DEVICE_MIN_BLOCK_SIZE (1)
39 #define VFS_DEVICE_MAX_BLOCK_SIZE (INT_MAX)
40 #define VFS_DEVICE_DEFAULT_BLOCK_SIZE (DISK_BLOCK_BYTES)
41 #define VFS_DEVICE_LABEL_SIZE (32768)
43 /* Allow comfortable room for another block and a header before PEOM */
44 #define EOM_EARLY_WARNING_ZONE_BLOCKS 4
46 /* Constants for free-space monitoring */
47 #define MONITOR_FREE_SPACE_EVERY_SECONDS 5
48 #define MONITOR_FREE_SPACE_EVERY_KB 102400
49 #define MONITOR_FREE_SPACE_CLOSELY_WITHIN_BLOCKS 128
51 /* This looks dangerous, but is actually modified by the umask. */
52 #define VFS_DEVICE_CREAT_MODE 0666
54 /* Possible (abstracted) results from a system I/O operation. */
57 RESULT_ERROR, /* Undefined error. */
58 RESULT_NO_DATA, /* End of File, while reading */
59 RESULT_NO_SPACE, /* Out of space. Sometimes we don't know if
60 it was this or I/O error, but this is the
61 preferred explanation. */
65 void vfs_device_register(void);
67 /* here are local prototypes */
68 static void vfs_device_init (VfsDevice * o);
69 static void vfs_device_class_init (VfsDeviceClass * c);
70 static void vfs_device_base_init (VfsDeviceClass * c);
71 static void vfs_device_finalize (GObject * o);
73 static gboolean vfs_device_start(Device * pself, DeviceAccessMode mode,
74 char * label, char * timestamp);
75 static gboolean vfs_device_finish (Device * pself);
76 static void vfs_device_open_device (Device * pself, char * device_name,
77 char * device_type, char * device_node);
78 static gboolean vfs_device_start_file (Device * pself, dumpfile_t * ji);
79 static gboolean vfs_device_finish_file (Device * dself);
80 static dumpfile_t * vfs_device_seek_file (Device * self, guint file);
81 static gboolean vfs_device_seek_block (Device * self, guint64 block);
82 static gboolean vfs_device_recycle_file (Device * pself, guint filenum);
83 static gboolean vfs_device_erase (Device * pself);
84 static Device * vfs_device_factory(char * device_name, char * device_type, char * device_node);
85 static DeviceStatusFlags vfs_device_read_label(Device * dself);
86 static gboolean vfs_device_write_block(Device * self, guint size, gpointer data);
87 static int vfs_device_read_block(Device * self, gpointer data, int * size_req);
88 static IoResult vfs_device_robust_write(VfsDevice * self, char *buf,
90 static IoResult vfs_device_robust_read(VfsDevice * self, char *buf,
93 /* Various helper functions. */
94 static void release_file(VfsDevice * self);
95 static gboolean check_is_dir(VfsDevice * self, const char * name);
96 static char * file_number_to_file_name(VfsDevice * self, guint file);
97 static gboolean file_number_to_file_name_functor(const char * filename,
99 static gboolean vfs_device_set_max_volume_usage_fn(Device *p_self,
100 DevicePropertyBase *base, GValue *val,
101 PropertySurety surety, PropertySource source);
102 static gboolean vfs_device_set_enforce_max_volume_usage_fn(Device *p_self,
103 DevicePropertyBase *base, GValue *val,
104 PropertySurety surety, PropertySource source);
105 static gboolean property_get_monitor_free_space_fn(Device *p_self,
106 DevicePropertyBase *base, GValue *val,
107 PropertySurety *surety, PropertySource *source);
108 static gboolean property_set_monitor_free_space_fn(Device *p_self,
109 DevicePropertyBase *base, GValue *val,
110 PropertySurety surety, PropertySource source);
111 static gboolean property_set_leom_fn(Device *p_self,
112 DevicePropertyBase *base, GValue *val,
113 PropertySurety surety, PropertySource source);
114 //static char* lockfile_name(VfsDevice * self, guint file);
115 static gboolean open_lock(VfsDevice * self, int file, gboolean exclusive);
116 static void promote_volume_lock(VfsDevice * self);
117 static void demote_volume_lock(VfsDevice * self);
118 static gboolean delete_vfs_files_functor(const char * filename,
120 static gboolean check_dir_empty_functor(const char * filename,
122 static gboolean clear_and_prepare_label(VfsDevice * self, char * label,
124 static int search_vfs_directory(VfsDevice *self, const char * regex,
125 SearchDirectoryFunctor functor, gpointer user_data);
126 static gint get_last_file_number(VfsDevice * self);
127 static gboolean get_last_file_number_functor(const char * filename,
129 static char * make_new_file_name(VfsDevice * self, const dumpfile_t * ji);
130 static gboolean try_unlink(const char * file);
132 /* return TRUE if the device is going to hit ENOSPC "soon" - this is used to
133 * detect LEOM as represented by actually running out of space on the
134 * underlying filesystem. Size is the size of the buffer that is about to
136 static gboolean check_at_leom(VfsDevice *self, guint64 size);
137 /* Similar, but for PEOM */
138 static gboolean check_at_peom(VfsDevice *self, guint64 size);
140 /* pointer to the classes of our parents */
141 static DeviceClass *parent_class = NULL;
143 /* device-specific properties */
144 DevicePropertyBase device_property_monitor_free_space;
145 #define PROPERTY_MONITOR_FREE_SPACE (device_property_monitor_free_space.ID)
147 void vfs_device_register(void) {
148 static const char * device_prefix_list[] = { "file", NULL };
150 device_property_fill_and_register(&device_property_monitor_free_space,
151 G_TYPE_BOOLEAN, "monitor_free_space",
152 "Should VFS device monitor the filesystem's available free space?");
154 register_device(vfs_device_factory, device_prefix_list);
158 vfs_device_get_type (void)
160 static GType type = 0;
162 if G_UNLIKELY(type == 0) {
163 static const GTypeInfo info = {
164 sizeof (VfsDeviceClass),
165 (GBaseInitFunc) vfs_device_base_init,
166 (GBaseFinalizeFunc) NULL,
167 (GClassInitFunc) vfs_device_class_init,
168 (GClassFinalizeFunc) NULL,
169 NULL /* class_data */,
172 (GInstanceInitFunc) vfs_device_init,
176 type = g_type_register_static (TYPE_DEVICE, "VfsDevice",
177 &info, (GTypeFlags)0);
184 vfs_device_init (VfsDevice * self) {
185 Device * dself = DEVICE(self);
188 self->dir_name = self->file_name = NULL;
189 self->open_file_fd = -1;
190 self->volume_bytes = 0;
191 self->volume_limit = 0;
193 self->enforce_volume_limit = TRUE;
195 self->monitor_free_space = TRUE;
196 self->checked_fs_free_bytes = G_MAXUINT64;
197 self->checked_fs_free_time = 0;
198 self->checked_fs_free_bytes = G_MAXUINT64;
200 /* Register Properties */
201 bzero(&response, sizeof(response));
203 g_value_init(&response, CONCURRENCY_PARADIGM_TYPE);
204 g_value_set_enum(&response, CONCURRENCY_PARADIGM_RANDOM_ACCESS);
205 device_set_simple_property(dself, PROPERTY_CONCURRENCY,
206 &response, PROPERTY_SURETY_GOOD, PROPERTY_SOURCE_DETECTED);
207 g_value_unset(&response);
209 g_value_init(&response, STREAMING_REQUIREMENT_TYPE);
210 g_value_set_enum(&response, STREAMING_REQUIREMENT_NONE);
211 device_set_simple_property(dself, PROPERTY_STREAMING,
212 &response, PROPERTY_SURETY_GOOD, PROPERTY_SOURCE_DETECTED);
213 g_value_unset(&response);
215 g_value_init(&response, G_TYPE_BOOLEAN);
216 g_value_set_boolean(&response, TRUE);
217 device_set_simple_property(dself, PROPERTY_APPENDABLE,
218 &response, PROPERTY_SURETY_GOOD, PROPERTY_SOURCE_DETECTED);
219 g_value_unset(&response);
221 g_value_init(&response, G_TYPE_BOOLEAN);
222 g_value_set_boolean(&response, TRUE);
223 device_set_simple_property(dself, PROPERTY_PARTIAL_DELETION,
224 &response, PROPERTY_SURETY_GOOD, PROPERTY_SOURCE_DETECTED);
225 g_value_unset(&response);
227 g_value_init(&response, G_TYPE_BOOLEAN);
228 g_value_set_boolean(&response, TRUE);
229 device_set_simple_property(dself, PROPERTY_FULL_DELETION,
230 &response, PROPERTY_SURETY_GOOD, PROPERTY_SOURCE_DETECTED);
231 g_value_unset(&response);
233 g_value_init(&response, G_TYPE_BOOLEAN);
234 g_value_set_boolean(&response, TRUE);
235 device_set_simple_property(dself, PROPERTY_LEOM,
236 &response, PROPERTY_SURETY_GOOD, PROPERTY_SOURCE_DETECTED);
237 g_value_unset(&response);
239 g_value_init(&response, G_TYPE_BOOLEAN);
240 g_value_set_boolean(&response, TRUE);
241 device_set_simple_property(dself, PROPERTY_ENFORCE_MAX_VOLUME_USAGE,
242 &response, PROPERTY_SURETY_GOOD, PROPERTY_SOURCE_DETECTED);
243 g_value_unset(&response);
245 g_value_init(&response, G_TYPE_BOOLEAN);
246 g_value_set_boolean(&response, FALSE);
247 device_set_simple_property(dself, PROPERTY_COMPRESSION,
248 &response, PROPERTY_SURETY_GOOD, PROPERTY_SOURCE_DETECTED);
249 g_value_unset(&response);
251 g_value_init(&response, MEDIA_ACCESS_MODE_TYPE);
252 g_value_set_enum(&response, MEDIA_ACCESS_MODE_READ_WRITE);
253 device_set_simple_property(dself, PROPERTY_MEDIUM_ACCESS_TYPE,
254 &response, PROPERTY_SURETY_GOOD, PROPERTY_SOURCE_DETECTED);
255 g_value_unset(&response);
259 vfs_device_class_init (VfsDeviceClass * c)
261 GObjectClass *g_object_class = (GObjectClass*) c;
262 DeviceClass *device_class = DEVICE_CLASS(c);
264 parent_class = g_type_class_ref(TYPE_DEVICE);
266 device_class->open_device = vfs_device_open_device;
267 device_class->start = vfs_device_start;
268 device_class->start_file = vfs_device_start_file;
269 device_class->read_label = vfs_device_read_label;
270 device_class->write_block = vfs_device_write_block;
271 device_class->read_block = vfs_device_read_block;
272 device_class->finish_file = vfs_device_finish_file;
273 device_class->seek_file = vfs_device_seek_file;
274 device_class->seek_block = vfs_device_seek_block;
275 device_class->recycle_file = vfs_device_recycle_file;
276 device_class->erase = vfs_device_erase;
277 device_class->finish = vfs_device_finish;
279 g_object_class->finalize = vfs_device_finalize;
283 vfs_device_base_init (VfsDeviceClass * c)
285 DeviceClass *device_class = (DeviceClass *)c;
287 device_class_register_property(device_class, PROPERTY_MONITOR_FREE_SPACE,
288 PROPERTY_ACCESS_GET_MASK | PROPERTY_ACCESS_SET_MASK,
289 property_get_monitor_free_space_fn,
290 property_set_monitor_free_space_fn);
292 device_class_register_property(device_class, PROPERTY_MAX_VOLUME_USAGE,
293 (PROPERTY_ACCESS_GET_MASK | PROPERTY_ACCESS_SET_MASK) &
294 (~ PROPERTY_ACCESS_SET_INSIDE_FILE_WRITE),
295 device_simple_property_get_fn,
296 vfs_device_set_max_volume_usage_fn);
298 device_class_register_property(device_class, PROPERTY_ENFORCE_MAX_VOLUME_USAGE,
299 (PROPERTY_ACCESS_GET_MASK | PROPERTY_ACCESS_SET_MASK) &
300 (~ PROPERTY_ACCESS_SET_INSIDE_FILE_WRITE),
301 device_simple_property_get_fn,
302 vfs_device_set_enforce_max_volume_usage_fn);
304 device_class_register_property(device_class, PROPERTY_COMPRESSION,
305 PROPERTY_ACCESS_GET_MASK,
306 device_simple_property_get_fn,
309 /* add the ability to set LEOM to FALSE, for testing purposes */
310 device_class_register_property(device_class, PROPERTY_LEOM,
311 PROPERTY_ACCESS_GET_MASK | PROPERTY_ACCESS_SET_BEFORE_START,
312 device_simple_property_get_fn,
313 property_set_leom_fn);
317 vfs_device_set_max_volume_usage_fn(Device *p_self,
318 DevicePropertyBase *base, GValue *val,
319 PropertySurety surety, PropertySource source)
321 VfsDevice *self = VFS_DEVICE(p_self);
323 self->volume_limit = g_value_get_uint64(val);
325 return device_simple_property_set_fn(p_self, base, val, surety, source);
329 vfs_device_set_enforce_max_volume_usage_fn(Device *p_self,
330 DevicePropertyBase *base, GValue *val,
331 PropertySurety surety, PropertySource source)
333 VfsDevice *self = VFS_DEVICE(p_self);
335 self->enforce_volume_limit = g_value_get_boolean(val);
337 return device_simple_property_set_fn(p_self, base, val, surety, source);
341 property_get_monitor_free_space_fn(Device *p_self, DevicePropertyBase *base G_GNUC_UNUSED,
342 GValue *val, PropertySurety *surety, PropertySource *source)
344 VfsDevice *self = VFS_DEVICE(p_self);
346 g_value_unset_init(val, G_TYPE_BOOLEAN);
347 g_value_set_boolean(val, self->monitor_free_space);
350 *surety = PROPERTY_SURETY_GOOD;
353 *source = PROPERTY_SOURCE_DEFAULT;
360 property_set_monitor_free_space_fn(Device *p_self,
361 DevicePropertyBase *base, GValue *val,
362 PropertySurety surety, PropertySource source)
364 VfsDevice *self = VFS_DEVICE(p_self);
366 self->monitor_free_space = g_value_get_boolean(val);
368 return device_simple_property_set_fn(p_self, base, val, surety, source);
372 property_set_leom_fn(Device *p_self,
373 DevicePropertyBase *base, GValue *val,
374 PropertySurety surety, PropertySource source)
376 VfsDevice *self = VFS_DEVICE(p_self);
378 self->leom = g_value_get_boolean(val);
380 return device_simple_property_set_fn(p_self, base, val, surety, source);
383 /* Drops everything associated with the volume file: Its name and fd. */
384 void release_file(VfsDevice * self) {
386 if (self->open_file_fd != -1)
387 robust_close(self->open_file_fd);
388 amfree(self->file_name);
390 self->open_file_fd = -1;
393 static void vfs_device_finalize(GObject * obj_self) {
394 VfsDevice *self = VFS_DEVICE (obj_self);
395 Device * d_self = (Device*)self;
397 if (d_self->access_mode != ACCESS_NULL) {
398 device_finish(d_self);
401 if(G_OBJECT_CLASS(parent_class)->finalize)
402 (* G_OBJECT_CLASS(parent_class)->finalize)(obj_self);
404 amfree(self->dir_name);
409 static Device * vfs_device_factory(char * device_name, char * device_type, char * device_node) {
411 g_assert(0 == strcmp(device_type, "file"));
412 rval = DEVICE(g_object_new(TYPE_VFS_DEVICE, NULL));
413 device_open_device(rval, device_name, device_type, device_node);
417 static gboolean check_is_dir(VfsDevice * self, const char * name) {
418 Device *dself = DEVICE(self);
419 struct stat dir_status;
421 if (stat(name, &dir_status) < 0) {
423 if (errno == EINTR) {
424 return check_is_dir(self, name);
427 device_set_error(dself,
428 vstrallocf(_("Error checking directory %s: %s"), name, strerror(errno)),
429 DEVICE_STATUS_DEVICE_ERROR);
431 } else if (!S_ISDIR(dir_status.st_mode)) {
432 device_set_error(dself,
433 vstrallocf(_("VFS Device path %s is not a directory"), name),
434 DEVICE_STATUS_DEVICE_ERROR);
447 /* A SearchDirectoryFunctor. */
448 static gboolean file_number_to_file_name_functor(const char * filename,
451 struct stat file_status;
452 fnfn_data *data = (fnfn_data*)datap;
454 result_tmp = vstralloc(data->self->dir_name, "/", filename, NULL);
456 /* Just to be thorough, let's check that it's a real
458 if (0 != stat(result_tmp, &file_status)) {
459 g_warning(_("Cannot stat file %s (%s), ignoring it"), result_tmp, strerror(errno));
460 } else if (!S_ISREG(file_status.st_mode)) {
461 g_warning(_("%s is not a regular file, ignoring it"), result_tmp);
464 if (data->result == NULL) {
465 data->result = result_tmp;
473 /* This function finds the filename for a given file number. We search
474 * for a filesystem file matching the regex /^0*$device_file\./; if
475 * there is more than one such file we make a warning and take an
477 static char * file_number_to_file_name(VfsDevice * self, guint device_file) {
485 regex = g_strdup_printf("^0*%u\\.", device_file);
487 search_vfs_directory(self, regex,
488 file_number_to_file_name_functor, &data);
492 if (data.count == 0) {
493 g_assert(data.result == NULL);
495 } else if (data.count > 1) {
496 g_warning("Found multiple names for file number %d, choosing file %s",
497 device_file, data.result);
500 g_assert(data.result != NULL);
503 g_assert_not_reached();
506 /* This function returns the dynamically-allocated lockfile name for a
507 given file number. */
509 static char * lockfile_name(VfsDevice * self, guint number) {
510 return g_strdup_printf("%s/%05d-lock", self->dir_name, number);
514 /* Does what you expect. If the lock already exists, it is released
515 * and regained, in case the mode is changing.
516 * The file field has several options:
517 * - file > 0: Open a lock on a real volume file.
518 * - file = 0: Open the volume lock as a volume file (for setup).
519 * - file < 0: Open the volume lock as a volume lock (persistantly).
521 static gboolean open_lock(G_GNUC_UNUSED VfsDevice * self,
522 G_GNUC_UNUSED int file,
523 G_GNUC_UNUSED gboolean exclusive) {
525 /* At the moment, file locking is horribly broken. */
529 /* For now, does it the bad way. */
530 static void promote_volume_lock(VfsDevice * self G_GNUC_UNUSED) {
533 static void demote_volume_lock(VfsDevice * self G_GNUC_UNUSED) {
536 /* A SearchDirectoryFunctor */
537 static gboolean update_volume_size_functor(const char * filename,
538 gpointer user_data) {
539 char * full_filename;
540 struct stat stat_buf;
541 VfsDevice * self = VFS_DEVICE(user_data);
543 full_filename = vstralloc(self->dir_name, "/", filename, NULL);
545 if (stat(full_filename, &stat_buf) < 0) {
546 /* Log it and keep going. */
547 g_warning(_("Couldn't stat file %s: %s"), full_filename, strerror(errno));
548 amfree(full_filename);
552 amfree(full_filename);
553 self->volume_bytes += stat_buf.st_size;
558 static void update_volume_size(VfsDevice * self) {
560 self->volume_bytes = 0;
561 search_vfs_directory(self, "^[0-9]+\\.",
562 update_volume_size_functor, self);
567 vfs_device_open_device (Device * pself, char * device_name, char * device_type, char * device_node) {
569 self = VFS_DEVICE(pself);
571 pself->min_block_size = VFS_DEVICE_MIN_BLOCK_SIZE;
572 pself->max_block_size = VFS_DEVICE_MAX_BLOCK_SIZE;
573 pself->block_size = VFS_DEVICE_DEFAULT_BLOCK_SIZE;
575 /* We don't have to free this ourselves; it will be freed by
576 * vfs_device_finalize whether we succeed here or not. */
577 self->dir_name = g_strconcat(device_node, "/data/", NULL);
579 if (parent_class->open_device) {
580 parent_class->open_device(pself, device_name, device_type, device_node);
584 /* A SearchDirectoryFunctor */
585 static gboolean delete_vfs_files_functor(const char * filename,
586 gpointer user_data) {
590 self = VFS_DEVICE(user_data);
592 /* Skip the volume lock. */
593 if (strcmp(filename, VOLUME_LOCKFILE_NAME) == 0)
596 path_name = vstralloc(self->dir_name, "/", filename, NULL);
597 if (unlink(path_name) != 0) {
598 g_warning(_("Error unlinking %s: %s"), path_name, strerror(errno));
604 /* delete_vfs_files deletes all VfsDevice files in the directory except the
606 void delete_vfs_files(VfsDevice * self) {
607 g_assert(self != NULL);
609 /* This function assumes that the volume is locked! */
610 search_vfs_directory(self, VFS_DEVICE_FILE_REGEX,
611 delete_vfs_files_functor, self);
614 /* This is a functor suitable for search_directory. It simply prints a
615 warning. It also dodges the volume lockfile. */
616 static gboolean check_dir_empty_functor(const char * filename,
617 gpointer user_data) {
618 VfsDevice * self = VFS_DEVICE(user_data);
621 if (strcmp(filename, VOLUME_LOCKFILE_NAME) == 0)
624 path_name = vstralloc(self->dir_name, "/", filename, NULL);
626 g_warning(_("Found spurious storage file %s"), path_name);
632 /* This function is used to write volume and dump headers. */
633 static gboolean write_amanda_header(VfsDevice * self,
634 const dumpfile_t * header) {
637 Device *d_self = DEVICE(self);
639 g_assert(header != NULL);
641 label_buffer = device_build_amanda_header(d_self, header, NULL);
643 amfree(label_buffer);
644 device_set_error(d_self,
645 stralloc(_("Amanda file header won't fit in a single block!")),
646 DEVICE_STATUS_DEVICE_ERROR);
650 result = vfs_device_robust_write(self, label_buffer, VFS_DEVICE_LABEL_SIZE);
651 /* vfs_device_robust_write sets error status if necessary */
652 amfree(label_buffer);
653 return (result == RESULT_SUCCESS);
656 /* clear_and_label will erase the contents of the directory, and write
657 * this label in its place. This function assumes we already have a volume
658 * label write lock in place (e.g., promote_lock() has been called.) */
659 static gboolean clear_and_prepare_label(VfsDevice * self, char * label,
661 dumpfile_t * label_header;
662 Device *d_self = DEVICE(self);
666 /* Delete any extant data, except our volume lock. */
667 delete_vfs_files(self);
669 /* Print warnings about any remaining files. */
670 search_vfs_directory(self, VFS_DEVICE_FILE_REGEX,
671 check_dir_empty_functor, self);
673 self->file_name = g_strdup_printf("%s/00000.%s", self->dir_name, label);
675 self->open_file_fd = robust_open(self->file_name,
676 O_CREAT | O_EXCL | O_WRONLY,
677 VFS_DEVICE_CREAT_MODE);
678 if (self->open_file_fd < 0) {
679 device_set_error(d_self,
680 vstrallocf(_("Can't open file %s: %s"), self->file_name, strerror(errno)),
681 DEVICE_STATUS_DEVICE_ERROR | DEVICE_STATUS_VOLUME_ERROR);
685 label_header = make_tapestart_header(DEVICE(self), label, timestamp);
686 if (!write_amanda_header(self, label_header)) {
687 /* write_amanda_header sets error status if necessary */
688 dumpfile_free(label_header);
691 dumpfile_free(d_self->volume_header);
692 d_self->header_block_size = VFS_DEVICE_LABEL_SIZE;
693 d_self->volume_header = label_header;
694 self->volume_bytes = VFS_DEVICE_LABEL_SIZE;
698 /* Just like search_directory, but returns -1 in the event of an error */
700 search_vfs_directory(
703 SearchDirectoryFunctor functor,
706 Device *dself = DEVICE(self);
710 dir_handle = opendir(self->dir_name);
711 if (dir_handle == NULL) {
712 device_set_error(dself,
713 vstrallocf(_("Couldn't open device %s (directory %s) for reading: %s"),
714 dself->device_name, self->dir_name, strerror(errno)),
715 DEVICE_STATUS_DEVICE_ERROR);
719 /* TODO: is this the right moment to acquire a lock?? */
721 result = search_directory(dir_handle, regex, functor, user_data);
725 closedir(dir_handle);
729 static DeviceStatusFlags vfs_device_read_label(Device * dself) {
730 VfsDevice * self = VFS_DEVICE(dself);
731 dumpfile_t * amanda_header;
733 g_assert(self != NULL);
735 if (!check_is_dir(self, self->dir_name)) {
736 /* error message set by check_is_dir */
737 return dself->status;
740 amfree(dself->volume_label);
741 amfree(dself->volume_time);
742 dumpfile_free(dself->volume_header);
743 dself->volume_header = NULL;
745 if (device_in_error(dself)) return dself->status;
747 amanda_header = dself->volume_header = vfs_device_seek_file(dself, 0);
749 if (amanda_header == NULL) {
750 /* This means an error occured getting locks or opening the header
752 device_set_error(dself,
753 stralloc("Error loading device header -- unlabeled volume?"),
754 DEVICE_STATUS_DEVICE_ERROR
755 | DEVICE_STATUS_VOLUME_ERROR
756 | DEVICE_STATUS_VOLUME_UNLABELED);
757 return dself->status;
760 /* close the fd we just opened */
761 vfs_device_finish_file(dself);
763 if (amanda_header->type != F_TAPESTART &&
764 amanda_header->type != F_EMPTY) {
765 /* This is an error, and should not happen. */
766 device_set_error(dself,
767 stralloc(_("Got a bad volume label")),
768 DEVICE_STATUS_VOLUME_ERROR);
769 amfree(amanda_header);
770 return dself->status;
773 /* self->volume_header is already set */
775 if (amanda_header->type == F_TAPESTART) {
776 dself->volume_label = g_strdup(amanda_header->name);
777 dself->volume_time = g_strdup(amanda_header->datestamp);
778 device_set_error(dself, NULL, DEVICE_STATUS_SUCCESS);
781 update_volume_size(self);
783 return dself->status;
786 static gboolean vfs_device_write_block(Device * pself, guint size, gpointer data) {
787 VfsDevice * self = VFS_DEVICE(pself);
790 if (device_in_error(self)) return FALSE;
792 g_assert(self->open_file_fd >= 0);
794 if (check_at_leom(self, size))
795 pself->is_eom = TRUE;
797 if (check_at_peom(self, size)) {
798 pself->is_eom = TRUE;
799 device_set_error(pself,
800 stralloc(_("No space left on device")),
801 DEVICE_STATUS_VOLUME_ERROR);
805 result = vfs_device_robust_write(self, data, size);
806 if (result != RESULT_SUCCESS) {
807 /* vfs_device_robust_write set error status appropriately */
811 self->volume_bytes += size;
812 self->checked_bytes_used += size;
819 vfs_device_read_block(Device * pself, gpointer data, int * size_req) {
824 self = VFS_DEVICE(pself);
826 if (device_in_error(self)) return -1;
828 if (data == NULL || (gsize)*size_req < pself->block_size) {
829 /* Just a size query. */
830 g_assert(pself->block_size < INT_MAX);
831 *size_req = (int)pself->block_size;
835 size = pself->block_size;
836 result = vfs_device_robust_read(self, data, &size);
843 pself->is_eof = TRUE;
844 pself->in_file = FALSE;
845 device_set_error(pself,
847 DEVICE_STATUS_SUCCESS);
850 device_set_error(pself,
851 vstrallocf(_("Error reading from data file: %s"), strerror(errno)),
852 DEVICE_STATUS_DEVICE_ERROR);
856 g_assert_not_reached();
860 vfs_device_start(Device * dself,
861 DeviceAccessMode mode, char * label,
863 VfsDevice * self = VFS_DEVICE(dself);
865 if (!check_is_dir(self, self->dir_name)) {
866 /* error message set by check_is_dir */
870 dself->in_file = FALSE;
872 if (mode == ACCESS_WRITE) {
873 promote_volume_lock(self);
874 if (!clear_and_prepare_label(self, label, timestamp)) {
875 /* clear_and_prepare_label sets error status if necessary */
876 demote_volume_lock(self);
880 dself->volume_label = newstralloc(dself->volume_label, label);
881 dself->volume_time = newstralloc(dself->volume_time, timestamp);
883 /* unset the VOLUME_UNLABELED flag, if it was set */
884 device_set_error(dself, NULL, DEVICE_STATUS_SUCCESS);
886 demote_volume_lock(self);
887 dself->access_mode = mode;
889 if (dself->volume_label == NULL && device_read_label(dself) != DEVICE_STATUS_SUCCESS) {
890 /* device_read_label already set our error message */
893 dself->access_mode = mode;
903 vfs_device_finish (Device * pself) {
905 self = VFS_DEVICE(pself);
909 pself->access_mode = ACCESS_NULL;
910 pself->in_file = FALSE;
912 if (device_in_error(self)) return FALSE;
922 /* A SearchDirectoryFunctor. */
923 static gboolean get_last_file_number_functor(const char * filename,
926 glfn_data * data = (glfn_data*)datap;
928 file = g_ascii_strtoull(filename, NULL, 10); /* Guaranteed to work. */
929 if (file > G_MAXINT) {
930 g_warning(_("Super-large device file %s found, ignoring"), filename);
933 /* This condition is needlessly complex due to sign issues. */
934 if (data->rval < 0 || ((guint)data->rval) < file) {
941 get_last_file_number(VfsDevice * self) {
944 Device *d_self = DEVICE(self);
948 count = search_vfs_directory(self, "^[0-9]+\\.",
949 get_last_file_number_functor, &data);
952 /* Somebody deleted something important while we weren't looking. */
953 device_set_error(d_self,
954 stralloc(_("Error identifying VFS device contents!")),
955 DEVICE_STATUS_DEVICE_ERROR | DEVICE_STATUS_VOLUME_ERROR);
958 g_assert(data.rval >= 0);
970 /* A SearchDirectoryFunctor. */
971 static gboolean get_next_file_number_functor(const char * filename,
974 gnfn_data * data = (gnfn_data*)datap;
976 file = g_ascii_strtoull(filename, NULL, 10); /* Guaranteed to work. */
977 if (file > G_MAXINT) {
978 g_warning(_("Super-large device file %s found, ignoring"), filename);
981 /* This condition is needlessly complex due to sign issues. */
982 if (file >= data->request &&
983 (data->best_found < 0 || file < (guint)data->best_found)) {
984 data->best_found = file;
989 /* Returns the file number equal to or greater than the given requested
992 get_next_file_number(VfsDevice * self, guint request) {
995 Device *d_self = DEVICE(self);
997 data.request = request;
998 data.best_found = -1;
1000 count = search_vfs_directory(self, "^[0-9]+\\.",
1001 get_next_file_number_functor, &data);
1004 /* Somebody deleted something important while we weren't looking. */
1005 device_set_error(d_self,
1006 stralloc(_("Error identifying VFS device contents!")),
1007 DEVICE_STATUS_DEVICE_ERROR | DEVICE_STATUS_VOLUME_ERROR);
1012 return data.best_found;
1015 /* Finds the file number, acquires a lock, and returns the new file name. */
1017 char * make_new_file_name(VfsDevice * self, const dumpfile_t * ji) {
1019 char *base, *sanitary_base;
1023 fileno = 1 + get_last_file_number(self);
1027 if (open_lock(self, fileno, TRUE)) {
1034 /* record that we're at this filenum now */
1035 DEVICE(self)->file = fileno;
1037 base = g_strdup_printf("%05d.%s.%s.%d", fileno, ji->name, ji->disk,
1039 sanitary_base = sanitise_filename(base);
1041 rval = g_strdup_printf("%s/%s", self->dir_name, sanitary_base);
1042 amfree(sanitary_base);
1047 vfs_device_start_file (Device * dself, dumpfile_t * ji) {
1048 VfsDevice * self = VFS_DEVICE(dself);
1050 dself->is_eom = FALSE;
1052 if (device_in_error(self)) return FALSE;
1054 /* set the blocksize in the header to 32k, since the VFS header is always
1055 * 32k regardless of the block_size setting */
1056 ji->blocksize = 32768;
1058 if (check_at_leom(self, VFS_DEVICE_LABEL_SIZE))
1059 dself->is_eom = TRUE;
1061 if (check_at_peom(self, VFS_DEVICE_LABEL_SIZE)) {
1062 dself->is_eom = TRUE;
1063 device_set_error(dself,
1064 stralloc(_("No space left on device")),
1065 DEVICE_STATUS_DEVICE_ERROR);
1069 /* The basic idea here is thus:
1070 1) Try to get a lock on the next filenumber.
1071 2) If that fails, update our idea of "next filenumber" and try again.
1072 3) Then open the file itself.
1076 self->file_name = make_new_file_name(self, ji);
1077 if (self->file_name == NULL) {
1078 device_set_error(dself,
1079 stralloc(_("Could not create header filename")),
1080 DEVICE_STATUS_DEVICE_ERROR);
1084 self->open_file_fd = robust_open(self->file_name,
1085 O_CREAT | O_EXCL | O_RDWR,
1086 VFS_DEVICE_CREAT_MODE);
1087 if (self->open_file_fd < 0) {
1088 device_set_error(dself,
1089 vstrallocf(_("Can't create file %s: %s"), self->file_name, strerror(errno)),
1090 DEVICE_STATUS_DEVICE_ERROR);
1096 if (!write_amanda_header(self, ji)) {
1097 /* write_amanda_header sets error status if necessary */
1102 /* handle some accounting business */
1103 self->volume_bytes += VFS_DEVICE_LABEL_SIZE;
1104 self->checked_bytes_used += VFS_DEVICE_LABEL_SIZE;
1105 dself->in_file = TRUE;
1107 /* make_new_file_name set pself->file for us */
1113 vfs_device_finish_file(Device * dself) {
1114 VfsDevice * self = VFS_DEVICE(dself);
1116 if (device_in_error(self)) return FALSE;
1120 dself->in_file = FALSE;
1125 /* This function is used for two purposes, rather than one. In
1126 * addition to its documented behavior, we also use it to open the
1127 * volume label for reading at startup. In that second case, we avoid
1128 * FdDevice-related side effects. */
1130 vfs_device_seek_file (Device * dself, guint requested_file) {
1131 VfsDevice *self = VFS_DEVICE(dself);
1134 char header_buffer[VFS_DEVICE_LABEL_SIZE];
1135 int header_buffer_size = sizeof(header_buffer);
1138 if (device_in_error(self)) return NULL;
1140 dself->in_file = FALSE;
1141 dself->is_eof = FALSE;
1146 if (requested_file > 0) {
1147 file = get_next_file_number(self, requested_file);
1149 file = requested_file;
1153 /* Did they request one past the end? */
1154 char * tmp_file_name;
1155 tmp_file_name = file_number_to_file_name(self, requested_file - 1);
1156 if (tmp_file_name != NULL) {
1157 free(tmp_file_name);
1158 dself->file = requested_file; /* other attributes are already correct */
1159 return make_tapeend_header();
1161 device_set_error(dself,
1162 stralloc(_("Attempt to read past tape-end file")),
1163 DEVICE_STATUS_SUCCESS);
1168 if (!open_lock(self, file, FALSE)) {
1169 device_set_error(dself,
1170 stralloc(_("could not acquire lock")),
1171 DEVICE_STATUS_DEVICE_ERROR);
1175 self->file_name = file_number_to_file_name(self, file);
1176 if (self->file_name == NULL) {
1177 device_set_error(dself,
1178 vstrallocf(_("File %d not found"), file),
1179 file == 0 ? DEVICE_STATUS_VOLUME_UNLABELED
1180 : DEVICE_STATUS_VOLUME_ERROR);
1182 rval = g_new(dumpfile_t, 1);
1187 self->open_file_fd = robust_open(self->file_name, O_RDONLY, 0);
1188 if (self->open_file_fd < 0) {
1189 device_set_error(dself,
1190 vstrallocf(_("Couldn't open file %s: %s"), self->file_name, strerror(errno)),
1191 DEVICE_STATUS_DEVICE_ERROR);
1192 amfree(self->file_name);
1197 result = vfs_device_robust_read(self, header_buffer,
1198 &header_buffer_size);
1199 if (result != RESULT_SUCCESS) {
1200 device_set_error(dself,
1201 vstrallocf(_("Problem reading Amanda header: %s"), device_error(dself)),
1202 DEVICE_STATUS_VOLUME_ERROR);
1207 rval = g_new(dumpfile_t, 1);
1208 parse_file_header(header_buffer, rval, header_buffer_size);
1209 switch (rval->type) {
1211 case F_CONT_DUMPFILE:
1212 case F_SPLIT_DUMPFILE:
1216 /* file 0 should have a TAPESTART header; vfs_device_read_label
1218 if (requested_file == 0)
1223 device_set_error(dself,
1224 stralloc(_("Invalid amanda header while reading file header")),
1225 DEVICE_STATUS_VOLUME_ERROR);
1231 /* update our state */
1232 if (requested_file == 0) {
1233 dself->header_block_size = header_buffer_size;
1235 dself->in_file = TRUE;
1242 vfs_device_seek_block (Device * pself, guint64 block) {
1246 self = VFS_DEVICE(pself);
1248 g_assert(self->open_file_fd >= 0);
1249 g_assert(sizeof(off_t) >= sizeof(guint64));
1250 if (device_in_error(self)) return FALSE;
1252 /* Pretty simple. We figure out the blocksize and use that. */
1253 result = lseek(self->open_file_fd,
1254 (block) * pself->block_size + VFS_DEVICE_LABEL_SIZE,
1257 pself->block = block;
1259 if (result == (off_t)(-1)) {
1260 device_set_error(pself,
1261 vstrallocf(_("Error seeking within file: %s"), strerror(errno)),
1262 DEVICE_STATUS_DEVICE_ERROR);
1269 static gboolean try_unlink(const char * file) {
1270 if (unlink(file) < 0) {
1278 check_at_leom(VfsDevice *self, guint64 size)
1280 gboolean recheck = FALSE;
1281 guint64 est_avail_now;
1282 struct fs_usage fsusage;
1283 guint64 block_size = DEVICE(self)->block_size;
1284 guint64 eom_warning_buffer = EOM_EARLY_WARNING_ZONE_BLOCKS * block_size;
1286 if (!self->leom || !self->monitor_free_space)
1289 /* handle VOLUME_LIMIT */
1290 if (self->enforce_volume_limit && self->volume_limit &&
1291 self->volume_bytes + size + eom_warning_buffer > self->volume_limit) {
1295 /* handle actual filesystem available space, using some heuristics to avoid polling this
1298 if (self->checked_fs_free_bytes >= self->checked_bytes_used + size)
1299 est_avail_now = self->checked_fs_free_bytes - self->checked_bytes_used - size;
1301 /* is it time to check again? */
1302 if (est_avail_now <= block_size * MONITOR_FREE_SPACE_CLOSELY_WITHIN_BLOCKS) {
1304 } else if (self->checked_bytes_used > MONITOR_FREE_SPACE_EVERY_KB * 1024) {
1306 } else if (self->checked_fs_free_time + MONITOR_FREE_SPACE_EVERY_SECONDS <= time(NULL)) {
1313 if (get_fs_usage(self->dir_name, NULL, &fsusage) < 0 || fsusage.fsu_bavail_top_bit_set) {
1314 g_warning("Filesystem cannot provide free space: %s; setting MONITOR_FREE_SPACE false",
1315 fsusage.fsu_bavail_top_bit_set? "no result" : strerror(errno));
1316 self->monitor_free_space = FALSE;
1320 self->checked_fs_free_bytes = fsusage.fsu_bavail * fsusage.fsu_blocksize;
1321 self->checked_bytes_used = 0;
1322 self->checked_fs_free_time = time(NULL);
1324 if (self->checked_fs_free_bytes - size <= eom_warning_buffer) {
1325 g_debug("%s: at LEOM", DEVICE(self)->device_name);
1333 check_at_peom(VfsDevice *self, guint64 size)
1335 if (self->enforce_volume_limit && (self->volume_limit > 0)) {
1336 guint64 newtotal = self->volume_bytes + size;
1337 if (newtotal > self->volume_limit) {
1346 vfs_device_recycle_file (Device * dself, guint filenum) {
1347 VfsDevice * self = VFS_DEVICE(dself);
1348 struct stat file_status;
1351 if (device_in_error(self)) return FALSE;
1354 * 1) Get a write lock on the file in question.
1355 * 2) Unlink the file in question.
1356 * 3) Unlink the lock.
1357 * 4) Release the lock.
1358 * FIXME: Is it OK to unlink the lockfile?
1361 self->file_name = file_number_to_file_name(self, filenum);
1362 if (self->file_name == NULL) {
1363 device_set_error(dself,
1364 vstrallocf(_("File %d not found"), filenum),
1365 DEVICE_STATUS_VOLUME_ERROR);
1369 if (!open_lock(self, filenum, FALSE)) {
1370 device_set_error(dself,
1371 stralloc(_("could not acquire lock")),
1372 DEVICE_STATUS_DEVICE_ERROR);
1376 if (0 != stat(self->file_name, &file_status)) {
1377 device_set_error(dself,
1378 vstrallocf(_("Cannot stat file %s (%s), so not removing"),
1379 self->file_name, strerror(errno)),
1380 DEVICE_STATUS_VOLUME_ERROR);
1383 file_size = file_status.st_size;
1385 if (!try_unlink(self->file_name)) {
1386 device_set_error(dself,
1387 vstrallocf(_("Unlink of %s failed: %s"), self->file_name, strerror(errno)),
1388 DEVICE_STATUS_VOLUME_ERROR);
1393 self->volume_bytes -= file_size;
1399 vfs_device_erase (Device * dself) {
1400 VfsDevice *self = VFS_DEVICE(dself);
1402 if (!open_lock(self, 0, TRUE))
1405 delete_vfs_files(self);
1409 dumpfile_free(dself->volume_header);
1410 dself->volume_header = NULL;
1411 device_set_error(dself, g_strdup("Unlabeled volume"),
1412 DEVICE_STATUS_VOLUME_UNLABELED);
1417 static IoResult vfs_device_robust_read(VfsDevice * self, char *buf,
1419 int fd = self->open_file_fd;
1420 Device *d_self = DEVICE(self);
1421 int want = *count, got = 0;
1423 while (got < want) {
1425 result = read(fd, buf + got, want - got);
1428 } else if (result == 0) {
1431 return RESULT_NO_DATA;
1434 return RESULT_SUCCESS;
1441 || errno == EWOULDBLOCK
1450 /* Error occured. */
1451 device_set_error(d_self,
1452 vstrallocf(_("Error reading fd %d: %s"), fd, strerror(errno)),
1453 DEVICE_STATUS_VOLUME_ERROR);
1455 return RESULT_ERROR;
1460 return RESULT_SUCCESS;
1464 vfs_device_robust_write(VfsDevice * self, char *buf, int count) {
1465 int fd = self->open_file_fd;
1466 Device *d_self = DEVICE(self);
1469 while (rval < count) {
1471 result = write(fd, buf + rval, count - rval);
1480 || errno == EWOULDBLOCK
1496 /* We are definitely out of space. */
1497 device_set_error(d_self,
1498 vstrallocf(_("No space left on device: %s"), strerror(errno)),
1499 DEVICE_STATUS_VOLUME_ERROR);
1500 return RESULT_NO_SPACE;
1502 /* Error occured. Note that here we handle EIO as an error. */
1503 device_set_error(d_self,
1504 vstrallocf(_("Error writing device fd %d: %s"), fd, strerror(errno)),
1505 DEVICE_STATUS_VOLUME_ERROR);
1506 return RESULT_ERROR;
1509 return RESULT_SUCCESS;
1512 /* TODO: add prop */