@@ -21,18 +21,21 @@ namespace ErrorCodes
2121 extern const int NOT_IMPLEMENTED;
2222 extern const int BAD_ARGUMENTS;
2323 extern const int CANNOT_UNLINK;
24+ extern const int CANNOT_RMDIR;
25+ extern const int READONLY;
2426}
2527
26- LocalObjectStorage::LocalObjectStorage (String key_prefix_ )
27- : key_prefix (std::move(key_prefix_ ))
28+ LocalObjectStorage::LocalObjectStorage (LocalObjectStorageSettings settings_ )
29+ : settings (std::move(settings_ ))
2830 , log(getLogger(" LocalObjectStorage" ))
2931{
3032 if (auto block_device_id = tryGetBlockDeviceId (" /" ); block_device_id.has_value ())
3133 description = *block_device_id;
3234 else
3335 description = " /" ;
3436
35- fs::create_directories (key_prefix);
37+ if (!settings.read_only )
38+ fs::create_directories (settings.key_prefix );
3639}
3740
3841bool LocalObjectStorage::exists (const StoredObject & object) const
@@ -69,6 +72,8 @@ std::unique_ptr<WriteBufferFromFileBase> LocalObjectStorage::writeObject( /// NO
6972 size_t buf_size,
7073 const WriteSettings & /* write_settings */ )
7174{
75+ throwIfReadonly ();
76+
7277 if (mode != WriteMode::Rewrite)
7378 throw Exception (ErrorCodes::BAD_ARGUMENTS, " LocalObjectStorage doesn't support append to files" );
7479
@@ -83,28 +88,52 @@ std::unique_ptr<WriteBufferFromFileBase> LocalObjectStorage::writeObject( /// NO
8388
8489void LocalObjectStorage::removeObject (const StoredObject & object) const
8590{
91+ throwIfReadonly ();
92+
8693 // / For local object storage files are actually removed when "metadata" is removed.
8794 if (!exists (object))
8895 return ;
8996
9097 if (0 != unlink (object.remote_path .data ()))
9198 ErrnoException::throwFromPath (ErrorCodes::CANNOT_UNLINK, object.remote_path , " Cannot unlink file {}" , object.remote_path );
99+
100+ // / Remove empty directories.
101+ fs::path dir = fs::path (object.remote_path ).parent_path ();
102+ fs::path root = fs::weakly_canonical (settings.key_prefix );
103+ while (dir.has_parent_path () && dir.has_relative_path () && dir != root && pathStartsWith (dir, root))
104+ {
105+ LOG_TEST (log, " Removing empty directory {}, has_parent_path: {}, has_relative_path: {}, root: {}, starts with root: {}" ,
106+ std::string (dir), dir.has_parent_path (), dir.has_relative_path (), std::string (root), pathStartsWith (dir, root));
107+
108+ std::string dir_str = dir;
109+ if (0 != rmdir (dir_str.data ()))
110+ {
111+ if (errno == ENOTDIR || errno == ENOTEMPTY)
112+ break ;
113+ ErrnoException::throwFromPath (ErrorCodes::CANNOT_RMDIR, dir_str, " Cannot remove directory {}" , dir_str);
114+ }
115+
116+ dir = dir.parent_path ();
117+ }
92118}
93119
94120void LocalObjectStorage::removeObjects (const StoredObjects & objects) const
95121{
122+ throwIfReadonly ();
96123 for (const auto & object : objects)
97124 removeObject (object);
98125}
99126
100127void LocalObjectStorage::removeObjectIfExists (const StoredObject & object)
101128{
129+ throwIfReadonly ();
102130 if (exists (object))
103131 removeObject (object);
104132}
105133
106134void LocalObjectStorage::removeObjectsIfExist (const StoredObjects & objects)
107135{
136+ throwIfReadonly ();
108137 for (const auto & object : objects)
109138 removeObjectIfExists (object);
110139}
@@ -113,14 +142,21 @@ ObjectMetadata LocalObjectStorage::getObjectMetadata(const std::string & path) c
113142{
114143 ObjectMetadata object_metadata;
115144 LOG_TEST (log, " Getting metadata for path: {}" , path);
145+
146+ auto time = fs::last_write_time (path);
147+
116148 object_metadata.size_bytes = fs::file_size (path);
149+ object_metadata.etag = std::to_string (std::chrono::duration_cast<std::chrono::nanoseconds>(time.time_since_epoch ()).count ());
117150 object_metadata.last_modified = Poco::Timestamp::fromEpochTime (
118- std::chrono::duration_cast<std::chrono::seconds>(fs::last_write_time (path) .time_since_epoch ()).count ());
151+ std::chrono::duration_cast<std::chrono::seconds>(time .time_since_epoch ()).count ());
119152 return object_metadata;
120153}
121154
122155void LocalObjectStorage::listObjects (const std::string & path, RelativePathsWithMetadata & children, size_t /* max_keys */ ) const
123156{
157+ if (!fs::is_directory (path))
158+ return ;
159+
124160 for (const auto & entry : fs::directory_iterator (path))
125161 {
126162 if (entry.is_directory ())
@@ -147,6 +183,7 @@ void LocalObjectStorage::copyObject( // NOLINT
147183 const WriteSettings & write_settings,
148184 std::optional<ObjectAttributes> /* object_to_attributes */ )
149185{
186+ throwIfReadonly ();
150187 auto in = readObject (object_from, read_settings);
151188 auto out = writeObject (object_to, WriteMode::Rewrite, /* attributes= */ {}, /* buf_size= */ DBMS_DEFAULT_BUFFER_SIZE, write_settings);
152189 copyData (*in, *out);
@@ -161,6 +198,12 @@ void LocalObjectStorage::startup()
161198{
162199}
163200
201+ void LocalObjectStorage::throwIfReadonly () const
202+ {
203+ if (settings.read_only )
204+ throw Exception (ErrorCodes::READONLY, " Local object storage `{}` is readonly" , getName ());
205+ }
206+
164207std::unique_ptr<IObjectStorage> LocalObjectStorage::cloneObjectStorage (
165208 const std::string & /* new_namespace */ ,
166209 const Poco::Util::AbstractConfiguration & /* config */ ,
@@ -173,7 +216,7 @@ ObjectStorageKey
173216LocalObjectStorage::generateObjectKeyForPath (const std::string & /* path */ , const std::optional<std::string> & /* key_prefix */ ) const
174217{
175218 constexpr size_t key_name_total_size = 32 ;
176- return ObjectStorageKey::createAsRelative (key_prefix, getRandomASCIIString (key_name_total_size));
219+ return ObjectStorageKey::createAsRelative (settings. key_prefix , getRandomASCIIString (key_name_total_size));
177220}
178221
179222}
0 commit comments