@@ -2226,3 +2226,225 @@ fn test_open_options_invalid_combinations() {
2226
2226
assert_eq ! ( err. kind( ) , ErrorKind :: InvalidInput ) ;
2227
2227
assert_eq ! ( err. to_string( ) , "must specify at least one of read, write, or append access" ) ;
2228
2228
}
2229
+
2230
+ #[ test]
2231
+ fn test_fs_set_times ( ) {
2232
+ #[ cfg( target_vendor = "apple" ) ]
2233
+ use crate :: os:: darwin:: fs:: FileTimesExt ;
2234
+ #[ cfg( windows) ]
2235
+ use crate :: os:: windows:: fs:: FileTimesExt ;
2236
+
2237
+ let tmp = tmpdir ( ) ;
2238
+ let path = tmp. join ( "foo" ) ;
2239
+ File :: create ( & path) . unwrap ( ) ;
2240
+
2241
+ let mut times = FileTimes :: new ( ) ;
2242
+ let accessed = SystemTime :: UNIX_EPOCH + Duration :: from_secs ( 12345 ) ;
2243
+ let modified = SystemTime :: UNIX_EPOCH + Duration :: from_secs ( 54321 ) ;
2244
+ times = times. set_accessed ( accessed) . set_modified ( modified) ;
2245
+
2246
+ #[ cfg( any( windows, target_vendor = "apple" ) ) ]
2247
+ let created = SystemTime :: UNIX_EPOCH + Duration :: from_secs ( 32123 ) ;
2248
+ #[ cfg( any( windows, target_vendor = "apple" ) ) ]
2249
+ {
2250
+ times = times. set_created ( created) ;
2251
+ }
2252
+
2253
+ match fs:: set_times ( & path, times) {
2254
+ // Allow unsupported errors on platforms which don't support setting times.
2255
+ #[ cfg( not( any(
2256
+ windows,
2257
+ all(
2258
+ unix,
2259
+ not( any(
2260
+ target_os = "android" ,
2261
+ target_os = "redox" ,
2262
+ target_os = "espidf" ,
2263
+ target_os = "horizon"
2264
+ ) )
2265
+ )
2266
+ ) ) ) ]
2267
+ Err ( e) if e. kind ( ) == ErrorKind :: Unsupported => return ,
2268
+ Err ( e) => panic ! ( "error setting file times: {e:?}" ) ,
2269
+ Ok ( _) => { }
2270
+ }
2271
+
2272
+ let metadata = fs:: metadata ( & path) . unwrap ( ) ;
2273
+ assert_eq ! ( metadata. accessed( ) . unwrap( ) , accessed) ;
2274
+ assert_eq ! ( metadata. modified( ) . unwrap( ) , modified) ;
2275
+ #[ cfg( any( windows, target_vendor = "apple" ) ) ]
2276
+ {
2277
+ assert_eq ! ( metadata. created( ) . unwrap( ) , created) ;
2278
+ }
2279
+ }
2280
+
2281
+ #[ test]
2282
+ fn test_fs_set_times_follows_symlink ( ) {
2283
+ #[ cfg( target_vendor = "apple" ) ]
2284
+ use crate :: os:: darwin:: fs:: FileTimesExt ;
2285
+ #[ cfg( windows) ]
2286
+ use crate :: os:: windows:: fs:: FileTimesExt ;
2287
+
2288
+ let tmp = tmpdir ( ) ;
2289
+
2290
+ // Create a target file
2291
+ let target = tmp. join ( "target" ) ;
2292
+ File :: create ( & target) . unwrap ( ) ;
2293
+
2294
+ // Create a symlink to the target
2295
+ #[ cfg( unix) ]
2296
+ let link = tmp. join ( "link" ) ;
2297
+ #[ cfg( unix) ]
2298
+ crate :: os:: unix:: fs:: symlink ( & target, & link) . unwrap ( ) ;
2299
+
2300
+ #[ cfg( windows) ]
2301
+ let link = tmp. join ( "link.txt" ) ;
2302
+ #[ cfg( windows) ]
2303
+ crate :: os:: windows:: fs:: symlink_file ( & target, & link) . unwrap ( ) ;
2304
+
2305
+ // Get the symlink's own times BEFORE calling set_times (to compare later)
2306
+ let link_metadata_before = fs:: symlink_metadata ( & link) . unwrap ( ) ;
2307
+ let link_accessed_before = link_metadata_before. accessed ( ) . unwrap ( ) ;
2308
+ let link_modified_before = link_metadata_before. modified ( ) . unwrap ( ) ;
2309
+
2310
+ let mut times = FileTimes :: new ( ) ;
2311
+ let accessed = SystemTime :: UNIX_EPOCH + Duration :: from_secs ( 12345 ) ;
2312
+ let modified = SystemTime :: UNIX_EPOCH + Duration :: from_secs ( 54321 ) ;
2313
+ times = times. set_accessed ( accessed) . set_modified ( modified) ;
2314
+
2315
+ #[ cfg( any( windows, target_vendor = "apple" ) ) ]
2316
+ let created = SystemTime :: UNIX_EPOCH + Duration :: from_secs ( 32123 ) ;
2317
+ #[ cfg( any( windows, target_vendor = "apple" ) ) ]
2318
+ {
2319
+ times = times. set_created ( created) ;
2320
+ }
2321
+
2322
+ // Call fs::set_times on the symlink - it should follow the link and modify the target
2323
+ match fs:: set_times ( & link, times) {
2324
+ // Allow unsupported errors on platforms which don't support setting times.
2325
+ #[ cfg( not( any(
2326
+ windows,
2327
+ all(
2328
+ unix,
2329
+ not( any(
2330
+ target_os = "android" ,
2331
+ target_os = "redox" ,
2332
+ target_os = "espidf" ,
2333
+ target_os = "horizon"
2334
+ ) )
2335
+ )
2336
+ ) ) ) ]
2337
+ Err ( e) if e. kind ( ) == ErrorKind :: Unsupported => return ,
2338
+ Err ( e) => panic ! ( "error setting file times through symlink: {e:?}" ) ,
2339
+ Ok ( _) => { }
2340
+ }
2341
+
2342
+ // Verify that the TARGET file's times were changed (following the symlink)
2343
+ let target_metadata = fs:: metadata ( & target) . unwrap ( ) ;
2344
+ assert_eq ! (
2345
+ target_metadata. accessed( ) . unwrap( ) ,
2346
+ accessed,
2347
+ "target file accessed time should match"
2348
+ ) ;
2349
+ assert_eq ! (
2350
+ target_metadata. modified( ) . unwrap( ) ,
2351
+ modified,
2352
+ "target file modified time should match"
2353
+ ) ;
2354
+ #[ cfg( any( windows, target_vendor = "apple" ) ) ]
2355
+ {
2356
+ assert_eq ! (
2357
+ target_metadata. created( ) . unwrap( ) ,
2358
+ created,
2359
+ "target file created time should match"
2360
+ ) ;
2361
+ }
2362
+
2363
+ // Also verify through the symlink (fs::metadata follows symlinks)
2364
+ let link_followed_metadata = fs:: metadata ( & link) . unwrap ( ) ;
2365
+ assert_eq ! ( link_followed_metadata. accessed( ) . unwrap( ) , accessed) ;
2366
+ assert_eq ! ( link_followed_metadata. modified( ) . unwrap( ) , modified) ;
2367
+
2368
+ // Verify that the SYMLINK ITSELF was NOT modified
2369
+ let link_metadata_after = fs:: symlink_metadata ( & link) . unwrap ( ) ;
2370
+ assert_eq ! (
2371
+ link_metadata_after. accessed( ) . unwrap( ) ,
2372
+ link_accessed_before,
2373
+ "symlink's own accessed time should not change"
2374
+ ) ;
2375
+ assert_eq ! (
2376
+ link_metadata_after. modified( ) . unwrap( ) ,
2377
+ link_modified_before,
2378
+ "symlink's own modified time should not change"
2379
+ ) ;
2380
+ }
2381
+
2382
+ #[ test]
2383
+ fn test_fs_set_times_nofollow ( ) {
2384
+ #[ cfg( target_vendor = "apple" ) ]
2385
+ use crate :: os:: darwin:: fs:: FileTimesExt ;
2386
+ #[ cfg( windows) ]
2387
+ use crate :: os:: windows:: fs:: FileTimesExt ;
2388
+
2389
+ let tmp = tmpdir ( ) ;
2390
+
2391
+ // Create a target file and a symlink to it
2392
+ let target = tmp. join ( "target" ) ;
2393
+ File :: create ( & target) . unwrap ( ) ;
2394
+
2395
+ #[ cfg( unix) ]
2396
+ let link = tmp. join ( "link" ) ;
2397
+ #[ cfg( unix) ]
2398
+ crate :: os:: unix:: fs:: symlink ( & target, & link) . unwrap ( ) ;
2399
+
2400
+ #[ cfg( windows) ]
2401
+ let link = tmp. join ( "link.txt" ) ;
2402
+ #[ cfg( windows) ]
2403
+ crate :: os:: windows:: fs:: symlink_file ( & target, & link) . unwrap ( ) ;
2404
+
2405
+ let mut times = FileTimes :: new ( ) ;
2406
+ let accessed = SystemTime :: UNIX_EPOCH + Duration :: from_secs ( 11111 ) ;
2407
+ let modified = SystemTime :: UNIX_EPOCH + Duration :: from_secs ( 22222 ) ;
2408
+ times = times. set_accessed ( accessed) . set_modified ( modified) ;
2409
+
2410
+ #[ cfg( any( windows, target_vendor = "apple" ) ) ]
2411
+ let created = SystemTime :: UNIX_EPOCH + Duration :: from_secs ( 33333 ) ;
2412
+ #[ cfg( any( windows, target_vendor = "apple" ) ) ]
2413
+ {
2414
+ times = times. set_created ( created) ;
2415
+ }
2416
+
2417
+ // Set times on the symlink itself (not following it)
2418
+ match fs:: set_times_nofollow ( & link, times) {
2419
+ // Allow unsupported errors on platforms which don't support setting times.
2420
+ #[ cfg( not( any(
2421
+ windows,
2422
+ all(
2423
+ unix,
2424
+ not( any(
2425
+ target_os = "android" ,
2426
+ target_os = "redox" ,
2427
+ target_os = "espidf" ,
2428
+ target_os = "horizon"
2429
+ ) )
2430
+ )
2431
+ ) ) ) ]
2432
+ Err ( e) if e. kind ( ) == ErrorKind :: Unsupported => return ,
2433
+ Err ( e) => panic ! ( "error setting symlink times: {e:?}" ) ,
2434
+ Ok ( _) => { }
2435
+ }
2436
+
2437
+ // Read symlink metadata (without following)
2438
+ let metadata = fs:: symlink_metadata ( & link) . unwrap ( ) ;
2439
+ assert_eq ! ( metadata. accessed( ) . unwrap( ) , accessed) ;
2440
+ assert_eq ! ( metadata. modified( ) . unwrap( ) , modified) ;
2441
+ #[ cfg( any( windows, target_vendor = "apple" ) ) ]
2442
+ {
2443
+ assert_eq ! ( metadata. created( ) . unwrap( ) , created) ;
2444
+ }
2445
+
2446
+ // Verify that the target file's times were NOT changed
2447
+ let target_metadata = fs:: metadata ( & target) . unwrap ( ) ;
2448
+ assert_ne ! ( target_metadata. accessed( ) . unwrap( ) , accessed) ;
2449
+ assert_ne ! ( target_metadata. modified( ) . unwrap( ) , modified) ;
2450
+ }
0 commit comments