diff --git a/gtk2_ardour/ardour_ui.cc b/gtk2_ardour/ardour_ui.cc index c210926b55..0a4a4782d2 100644 --- a/gtk2_ardour/ardour_ui.cc +++ b/gtk2_ardour/ardour_ui.cc @@ -1050,16 +1050,21 @@ ARDOUR_UI::update_disk_space() return; } - framecnt_t frames = _session->available_capture_duration(); + boost::optional opt_frames = _session->available_capture_duration(); char buf[64]; framecnt_t fr = _session->frame_rate(); - if (frames == max_framecnt) { + if (!opt_frames) { + /* Available space is unknown */ + snprintf (buf, sizeof (buf), "%s", _("Disk: Unknown")); + } else if (opt_frames.get_value_or (0) == max_framecnt) { snprintf (buf, sizeof (buf), "%s", _("Disk: 24hrs+")); } else { rec_enabled_streams = 0; _session->foreach_route (this, &ARDOUR_UI::count_recenabled_streams); + framecnt_t frames = opt_frames.get_value_or (0); + if (rec_enabled_streams) { frames /= rec_enabled_streams; } diff --git a/libs/ardour/ardour/session.h b/libs/ardour/ardour/session.h index 9dd19eb2a1..f7f8516ef3 100644 --- a/libs/ardour/ardour/session.h +++ b/libs/ardour/ardour/session.h @@ -669,7 +669,7 @@ class Session : public PBD::StatefulDestructible, public PBD::ScopedConnectionLi /* s/w "RAID" management */ - framecnt_t available_capture_duration(); + boost::optional available_capture_duration(); /* I/O bundles */ @@ -1329,16 +1329,21 @@ class Session : public PBD::StatefulDestructible, public PBD::ScopedConnectionLi /* S/W RAID */ struct space_and_path { - uint32_t blocks; /* 4kB blocks */ + uint32_t blocks; ///< 4kB blocks + bool blocks_unknown; ///< true if blocks is unknown std::string path; - space_and_path() { - blocks = 0; - } + space_and_path () + : blocks (0) + , blocks_unknown (true) + {} }; struct space_and_path_ascending_cmp { bool operator() (space_and_path a, space_and_path b) { + if (a.blocks_unknown != b.blocks_unknown) { + return !a.blocks_unknown; + } return a.blocks > b.blocks; } }; @@ -1348,6 +1353,11 @@ class Session : public PBD::StatefulDestructible, public PBD::ScopedConnectionLi std::vector session_dirs; std::vector::iterator last_rr_session_dir; uint32_t _total_free_4k_blocks; + /** If this is true, _total_free_4k_blocks is not definite, + as one or more of the session directories' filesystems + could not report free space. + */ + bool _total_free_4k_blocks_uncertain; Glib::Mutex space_lock; bool no_questions_about_missing_files; diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index 9267832cb1..f78a26cc05 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -143,6 +143,7 @@ Session::Session (AudioEngine &eng, , _all_route_group (new RouteGroup (*this, "all")) , routes (new RouteList) , _total_free_4k_blocks (0) + , _total_free_4k_blocks_uncertain (false) , _bundles (new BundleList) , _bundle_xml_node (0) , _current_trans (0) @@ -3501,9 +3502,16 @@ Session::graph_reordered () } } -framecnt_t +/** @return Number of frames that there is disk space available to write, + * if known. + */ +boost::optional Session::available_capture_duration () { + if (_total_free_4k_blocks_uncertain) { + return boost::optional (); + } + float sample_bytes_on_disk = 4.0; // keep gcc happy switch (config.get_native_file_data_format()) { diff --git a/libs/ardour/session_state.cc b/libs/ardour/session_state.cc index e729a7b00d..30f8571c01 100644 --- a/libs/ardour/session_state.cc +++ b/libs/ardour/session_state.cc @@ -46,6 +46,10 @@ #include #endif +#ifdef HAVE_SYS_STATVFS_H +#include +#endif + #include #include @@ -2079,23 +2083,48 @@ Session::save_template (string template_name) void Session::refresh_disk_space () { -#if HAVE_SYS_VFS_H - struct statfs statfsbuf; - vector::iterator i; +#if HAVE_SYS_VFS_H && HAVE_SYS_STATVFS_H + Glib::Mutex::Lock lm (space_lock); - double scale; /* get freespace on every FS that is part of the session path */ _total_free_4k_blocks = 0; + _total_free_4k_blocks_uncertain = false; - for (i = session_dirs.begin(); i != session_dirs.end(); ++i) { - statfs ((*i).path.c_str(), &statfsbuf); + for (vector::iterator i = session_dirs.begin(); i != session_dirs.end(); ++i) { - scale = statfsbuf.f_bsize/4096.0; + struct statfs statfsbuf; + statfs (i->path.c_str(), &statfsbuf); - (*i).blocks = (uint32_t) floor (statfsbuf.f_bavail * scale); - _total_free_4k_blocks += (*i).blocks; + double const scale = statfsbuf.f_bsize / 4096.0; + + /* See if this filesystem is read-only */ + struct statvfs statvfsbuf; + statvfs (i->path.c_str(), &statvfsbuf); + + /* f_bavail can be 0 if it is undefined for whatever + filesystem we are looking at; Samba shares mounted + via GVFS are an example of this. + */ + if (statfsbuf.f_bavail == 0) { + /* block count unknown */ + i->blocks = 0; + i->blocks_unknown = true; + } else if (statvfsbuf.f_flag & ST_RDONLY) { + /* read-only filesystem */ + i->blocks = 0; + i->blocks_unknown = false; + } else { + /* read/write filesystem with known space */ + i->blocks = (uint32_t) floor (statfsbuf.f_bavail * scale); + i->blocks_unknown = false; + } + + _total_free_4k_blocks += i->blocks; + if (i->blocks_unknown) { + _total_free_4k_blocks_uncertain = true; + } } #endif } diff --git a/libs/ardour/wscript b/libs/ardour/wscript index c0fcec8d29..25a2cb6d92 100644 --- a/libs/ardour/wscript +++ b/libs/ardour/wscript @@ -289,6 +289,7 @@ def configure(conf): conf.define('CURRENT_SESSION_FILE_VERSION', CURRENT_SESSION_FILE_VERSION) conf.check(header_name='sys/vfs.h', define_name='HAVE_SYS_VFS_H',mandatory=False) + conf.check(header_name='sys/statvfs.h', define_name='HAVE_SYS_STATVFS_H',mandatory=False) conf.check(header_name='jack/session.h', uselib = [ 'JACK' ], define_name='HAVE_JACK_SESSION')