1 = Directories = 2 3 Filesystem directories contain collections of files, other directories, along 4 with other filesystem objects. Directories can be listed, permitting 5 filesystem clients to peruse the contents of each directory. However, the act 6 of listing a directory may impose certain constraints on the operations being 7 conducted on the directory during the listing process. 8 9 Operations that affect directory contents include the following: 10 11 * Creating a new object 12 * Removing an object 13 * Moving an object out of the directory 14 * Moving an object into the directory 15 16 It is conceivable that for some filesystems, these operations will not be able 17 to rely on directly modifying the directory since a listing operation may be 18 in progress. Consequently, a number of rules would be imposed for such 19 filesystems. It appears that Ext2-based filesystems accessed via libext2fs do 20 not require such rules to be applied outside the library due to the design of 21 the filesystem structures and the behaviour of the library itself. 22 23 == Providers == 24 25 Providers of filesystem objects are created when at least one filesystem 26 client is accessing such an object, with the object regarded as being 27 '''open'''. By definition, a provider will exist until all clients have closed 28 or discarded their means of accessing the object. 29 30 An object is regarded as '''accessible''' if it can still be opened by 31 filesystem clients. Where an object has been removed, it will no longer be 32 accessible since clients should not be able to see the object in the 33 filesystem any more. (The actual filesystem residing in storage may not have 34 been updated for such a removal, this being the eventual outcome of a removal 35 operation.) 36 37 Providers can be regarded as '''accessible''' if they maintain access to 38 objects that are open and accessible. Clients opening objects will only gain 39 access to accessible providers. Providers can become inaccessible if an object 40 is removed even if the object (and its provider) is still open. 41 42 Initially, providers will correspond to objects resident in the stored 43 filesystem. Thus, looking up an object using its path will involve the 44 filesystem, yielding a file identifier that can be used to map to a provider. 45 46 {{{ 47 def get_provider_from_filesystem(path): 48 fileid = opening.get_fileid(path) 49 50 if not fileid: 51 return error 52 53 return find_provider(fileid) 54 }}} 55 56 However, providers may not always immediately correspond to objects resident 57 in the stored filesystem. Where an object is created but cannot be immediately 58 registered in the contents of a directory, it must be registered separately 59 and attempts to open this object must consult this separate mapping of 60 filenames to providers. 61 62 {{{ 63 def get_provider(path): 64 provider = find_created_provider(path) 65 66 if provider: 67 return provider 68 69 return get_provider_from_filesystem(path) 70 }}} 71 72 Providers that are inaccessible need to be retained until they are closed. 73 However, since they are no longer accessible, they should not be available to 74 the provider lookup operations. 75 76 When providers are closed, they are removed from any mapping in which they 77 could be found. Inaccessible providers that have been retained outside the 78 identifier or filename mappings will represent objects that should be removed 79 from their parent directory. Accessible providers retained by the mappings 80 will represent objects that should be created in their parent directory. 81 82 Whether object removal or creation will occur depends on whether the parent 83 directory is able to safely perform these operations concurrently with other 84 operations (such as servicing a listing operation) or whether such operations 85 will need to be deferred until they can be safely performed. 86 87 == Object Removal == 88 89 Filesystem object removal involves obtaining any provider of the object. Where 90 a provider can be obtained, it will be made inaccessible and removal will be 91 requested. The actual object will not be removed at least until it is closed 92 because it may still receive and provide data. (Unlinking open files is a 93 feature of Unix systems.) 94 95 Where a provider cannot be obtained, an attempt will be made to obtain the 96 parent directory provider, and if this succeeds, it indicates that the 97 directory is being accessed and must therefore be notified of the intention to 98 eventually remove the object concerned. 99 100 Without any provider of the object, and where no directory provider can be 101 obtained, the object can be immediately removed from the filesystem. 102 103 {{{ 104 def remove_object(path): 105 provider = get_provider(path) 106 107 if provider: 108 return make_provider_inaccessible(provider) 109 110 dirname, basename = split(path) 111 directory_provider = get_provider(dirname) 112 113 if directory_provider: 114 return directory_provider.remove_pending(basename) 115 116 return filesystem.remove_object(path) 117 }}} 118 119 It should be noted that with no accessible provider, any attempt to create a 120 file with the same name as a removed file should cause a new "version" of the 121 file to be created. 122 123 == Object Creation == 124 125 Filesystem object creation occurs when no existing provider can be found for a 126 named object and where creation is requested. Any new object will be 127 accessible and remain so until or unless it is removed. 128 129 Whether an object is created in the filesystem storage depends on whether the 130 parent directory is being used. 131 132 If a parent directory is not being used, the object can be registered in its 133 proper location in the filesystem itself, yielding a file identifier. 134 135 If a parent directory is being used, the object cannot be immediately 136 registered in its proper location, but since data may still need to be written 137 to storage, a temporary location is employed, yielding a file identifier. 138 139 For an object created in a temporary location, a temporary mapping from the 140 path of the object to the provider is established, with the parent directory 141 being notified of the pending object creation. 142 143 {{{ 144 def create_object(path): 145 provider = get_provider(path) 146 147 if provider: 148 return error 149 150 dirname, basename = split(path) 151 directory_provider = get_provider(dirname) 152 153 if directory_provider: 154 provider = make_provider(get_temporary_location(path)) 155 directory_provider.create_pending(provider) 156 return register_created_provider(path, provider) 157 158 provider = make_provider(path) 159 return register_provider(provider) 160 }}} 161 162 == Created Providers == 163 164 Created providers are retained by the registry until their files can be 165 committed to the filesystem in the desired location. 166 167 The `register_created_provider` operation establishes a mapping from a path to 168 a provider that can be queried using the `find_created_provider` operation. 169 170 Created providers are deregistered when they are closed. This will occur when 171 the parent directory of the file to be committed is closed. At that time, the 172 created file will be moved from its temporary location to the desired 173 location. 174 175 == Inaccessible Providers == 176 177 Inaccessible providers are retained by the registry until they are closed. 178 179 Where the provided file resides in a non-temporary location, closure will 180 occur when the parent directory of the provided file is closed, this having 181 obstructed the removal of the file. At that time, the provided file will be 182 removed. 183 184 Where the provided file resides in a temporary location, closure will not be 185 obstructed by any directory and will cause the file to be removed immediately. 186 187 == Directory Provider Closure == 188 189 A critical event that typically initiates housekeeping work for created and 190 removed files is the closure of the parent directory of those files. 191 192 Directory providers support the `create_pending` and `remove_pending` 193 operations that register the details of affected files. When closure occurs, 194 the provider will contact the registry to initiate the necessary work to 195 relocate created files and to remove files. 196 197 198 199 200 Where an object provider is notified of pending removal, it will initiate 201 removal of the actual object upon closure, checking for an active parent 202 provider. 203 204 Where a parent provider is notified of pending removal, it will initiate 205 removal of the actual object upon closure, checking for an active object 206 provider. 207 208 Object opening logic would need to be adjusted. Consider the case where a 209 file is removed but still open. 210 211 Any attempt to open a file with the same name must ignore the original and 212 open a new file of that name which would be stored elsewhere until such 213 time as the original is actually removed from its location. 214 215 This new file might have a different identifier since the original file 216 would still exist and retain the identifier. 217 218 Any such new, but uncommitted, file must be accessible by other clients 219 attempting to open a file of that name. However, removal operations must 220 also be possible, making this version of the open file also unaccessible 221 to new opening clients. 222 223 Therefore, any providers used by opening clients must only refer to a 224 version of a file that is pending removal if no other version has been 225 created. Once a new version has been created, any provider pending removal 226 must be relegated to a separate collection. 227 228 Storage of new versions of files could be confined to special directories 229 that are hidden from clients. 230 231 The opening logic would be as follows: 232 233 provider = find_provider(path) 234 235 if provider: 236 if provider.pending_removal(): 237 relegate(provider) 238 provider = create_provider(path, hidden=True) 239 else: 240 provider = create_provider(path, hidden=False) 241 242 # Have provider. 243 244 return provider.make_resource() 245 246 A provider would normally be obtained by consulting the filesystem 247 implementation. However, new versions of files replacing ones pending 248 removal will reside in locations that are different to the intended 249 location of the file. Consequently, the registry needs to maintain its own 250 mapping of paths to providers for such file versions. 251 252 The opportunity to remove a file definitively arises when the following 253 conditions are satisfied: 254 255 * The parent directory is not open in some way 256 * The original provider for the file is no longer open 257 258 For new versions of a removed file that have themselves been marked as 259 pending removal, their closure is sufficient to remove their filesystem 260 resources. 261 262 However, the registry must maintain a path entry for any active version 263 of a removed file until that version is closed. Thus, the following 264 conditions apply: 265 266 * The parent directory (of the original file) is not open in some way 267 * The provider of the new version is no longer open 268 269 It will not be generally possible to open the hidden directory containing 270 new file versions. Therefore, the transfer of the file to its intended 271 location will not risk interfering with any operations on the hidden 272 directory. 273