gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/pkg/p9/messages_test.go (about) 1 // Copyright 2018 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package p9 16 17 import ( 18 "fmt" 19 "reflect" 20 "testing" 21 ) 22 23 func TestEncodeDecode(t *testing.T) { 24 objs := []encoder{ 25 &QID{ 26 Type: 1, 27 Version: 2, 28 Path: 3, 29 }, 30 &FSStat{ 31 Type: 1, 32 BlockSize: 2, 33 Blocks: 3, 34 BlocksFree: 4, 35 BlocksAvailable: 5, 36 Files: 6, 37 FilesFree: 7, 38 FSID: 8, 39 NameLength: 9, 40 }, 41 &AttrMask{ 42 Mode: true, 43 NLink: true, 44 UID: true, 45 GID: true, 46 RDev: true, 47 ATime: true, 48 MTime: true, 49 CTime: true, 50 INo: true, 51 Size: true, 52 Blocks: true, 53 BTime: true, 54 Gen: true, 55 DataVersion: true, 56 }, 57 &Attr{ 58 Mode: Exec, 59 UID: 2, 60 GID: 3, 61 NLink: 4, 62 RDev: 5, 63 Size: 6, 64 BlockSize: 7, 65 Blocks: 8, 66 ATimeSeconds: 9, 67 ATimeNanoSeconds: 10, 68 MTimeSeconds: 11, 69 MTimeNanoSeconds: 12, 70 CTimeSeconds: 13, 71 CTimeNanoSeconds: 14, 72 BTimeSeconds: 15, 73 BTimeNanoSeconds: 16, 74 Gen: 17, 75 DataVersion: 18, 76 }, 77 &SetAttrMask{ 78 Permissions: true, 79 UID: true, 80 GID: true, 81 Size: true, 82 ATime: true, 83 MTime: true, 84 CTime: true, 85 ATimeNotSystemTime: true, 86 MTimeNotSystemTime: true, 87 }, 88 &SetAttr{ 89 Permissions: 1, 90 UID: 2, 91 GID: 3, 92 Size: 4, 93 ATimeSeconds: 5, 94 ATimeNanoSeconds: 6, 95 MTimeSeconds: 7, 96 MTimeNanoSeconds: 8, 97 }, 98 &Dirent{ 99 QID: QID{Type: 1}, 100 Offset: 2, 101 Type: 3, 102 Name: "a", 103 }, 104 &Rlerror{ 105 Error: 1, 106 }, 107 &Tstatfs{ 108 FID: 1, 109 }, 110 &Rstatfs{ 111 FSStat: FSStat{Type: 1}, 112 }, 113 &Tlopen{ 114 FID: 1, 115 Flags: WriteOnly, 116 }, 117 &Rlopen{ 118 QID: QID{Type: 1}, 119 IoUnit: 2, 120 }, 121 &Tbind{ 122 Directory: 1, 123 SockType: 2, 124 SockName: "name", 125 GID: 3, 126 UID: 4, 127 NewFID: 5, 128 }, 129 &Rbind{ 130 QID: QID{Type: 1}, 131 }, 132 &Tlconnect{ 133 FID: 1, 134 }, 135 &Rlconnect{}, 136 &Tlcreate{ 137 FID: 1, 138 Name: "a", 139 OpenFlags: 2, 140 Permissions: 3, 141 GID: 4, 142 }, 143 &Rlcreate{ 144 Rlopen{QID: QID{Type: 1}}, 145 }, 146 &Tsymlink{ 147 Directory: 1, 148 Name: "a", 149 Target: "b", 150 GID: 2, 151 }, 152 &Rsymlink{ 153 QID: QID{Type: 1}, 154 }, 155 &Tmknod{ 156 Directory: 1, 157 Name: "a", 158 Mode: 2, 159 Major: 3, 160 Minor: 4, 161 GID: 5, 162 }, 163 &Rmknod{ 164 QID: QID{Type: 1}, 165 }, 166 &Trename{ 167 FID: 1, 168 Directory: 2, 169 Name: "a", 170 }, 171 &Rrename{}, 172 &Treadlink{ 173 FID: 1, 174 }, 175 &Rreadlink{ 176 Target: "a", 177 }, 178 &Tgetattr{ 179 FID: 1, 180 AttrMask: AttrMask{Mode: true}, 181 }, 182 &Rgetattr{ 183 Valid: AttrMask{Mode: true}, 184 QID: QID{Type: 1}, 185 Attr: Attr{Mode: Write}, 186 }, 187 &Tsetattr{ 188 FID: 1, 189 Valid: SetAttrMask{Permissions: true}, 190 SetAttr: SetAttr{Permissions: Write}, 191 }, 192 &Rsetattr{}, 193 &Txattrwalk{ 194 FID: 1, 195 NewFID: 2, 196 Name: "a", 197 }, 198 &Rxattrwalk{ 199 Size: 1, 200 }, 201 &Txattrcreate{ 202 FID: 1, 203 Name: "a", 204 AttrSize: 2, 205 Flags: 3, 206 }, 207 &Rxattrcreate{}, 208 &Tgetxattr{ 209 FID: 1, 210 Name: "abc", 211 Size: 2, 212 }, 213 &Rgetxattr{ 214 Value: "xyz", 215 }, 216 &Tsetxattr{ 217 FID: 1, 218 Name: "abc", 219 Value: "xyz", 220 Flags: 2, 221 }, 222 &Rsetxattr{}, 223 &Treaddir{ 224 Directory: 1, 225 DirentOffset: 2, 226 Count: 3, 227 }, 228 &Rreaddir{ 229 // Count must be sufficient to encode a dirent. 230 Count: 0x1a, 231 Entries: []Dirent{{QID: QID{Type: 2}}}, 232 }, 233 &Tfsync{ 234 FID: 1, 235 }, 236 &Rfsync{}, 237 &Tlink{ 238 Directory: 1, 239 Target: 2, 240 Name: "a", 241 }, 242 &Rlink{}, 243 &Tmkdir{ 244 Directory: 1, 245 Name: "a", 246 Permissions: 2, 247 GID: 3, 248 }, 249 &Rmkdir{ 250 QID: QID{Type: 1}, 251 }, 252 &Trenameat{ 253 OldDirectory: 1, 254 OldName: "a", 255 NewDirectory: 2, 256 NewName: "b", 257 }, 258 &Rrenameat{}, 259 &Tunlinkat{ 260 Directory: 1, 261 Name: "a", 262 Flags: 2, 263 }, 264 &Runlinkat{}, 265 &Tversion{ 266 MSize: 1, 267 Version: "a", 268 }, 269 &Rversion{ 270 MSize: 1, 271 Version: "a", 272 }, 273 &Tauth{ 274 AuthenticationFID: 1, 275 UserName: "a", 276 AttachName: "b", 277 UID: 2, 278 }, 279 &Rauth{ 280 QID: QID{Type: 1}, 281 }, 282 &Tattach{ 283 FID: 1, 284 Auth: Tauth{AuthenticationFID: 2}, 285 }, 286 &Rattach{ 287 QID: QID{Type: 1}, 288 }, 289 &Tflush{ 290 OldTag: 1, 291 }, 292 &Rflush{}, 293 &Twalk{ 294 FID: 1, 295 NewFID: 2, 296 Names: []string{"a"}, 297 }, 298 &Rwalk{ 299 QIDs: []QID{{Type: 1}}, 300 }, 301 &Tread{ 302 FID: 1, 303 Offset: 2, 304 Count: 3, 305 }, 306 &Rread{ 307 Data: []byte{'a'}, 308 }, 309 &Twrite{ 310 FID: 1, 311 Offset: 2, 312 Data: []byte{'a'}, 313 }, 314 &Rwrite{ 315 Count: 1, 316 }, 317 &Tclunk{ 318 FID: 1, 319 }, 320 &Rclunk{}, 321 &Tremove{ 322 FID: 1, 323 }, 324 &Rremove{}, 325 &Tflushf{ 326 FID: 1, 327 }, 328 &Rflushf{}, 329 &Twalkgetattr{ 330 FID: 1, 331 NewFID: 2, 332 Names: []string{"a"}, 333 }, 334 &Rwalkgetattr{ 335 QIDs: []QID{{Type: 1}}, 336 Valid: AttrMask{Mode: true}, 337 Attr: Attr{Mode: Write}, 338 }, 339 &Tucreate{ 340 Tlcreate: Tlcreate{ 341 FID: 1, 342 Name: "a", 343 OpenFlags: 2, 344 Permissions: 3, 345 GID: 4, 346 }, 347 UID: 5, 348 }, 349 &Rucreate{ 350 Rlcreate{Rlopen{QID: QID{Type: 1}}}, 351 }, 352 &Tumkdir{ 353 Tmkdir: Tmkdir{ 354 Directory: 1, 355 Name: "a", 356 Permissions: 2, 357 GID: 3, 358 }, 359 UID: 4, 360 }, 361 &Rumkdir{ 362 Rmkdir{QID: QID{Type: 1}}, 363 }, 364 &Tusymlink{ 365 Tsymlink: Tsymlink{ 366 Directory: 1, 367 Name: "a", 368 Target: "b", 369 GID: 2, 370 }, 371 UID: 3, 372 }, 373 &Rusymlink{ 374 Rsymlink{QID: QID{Type: 1}}, 375 }, 376 &Tumknod{ 377 Tmknod: Tmknod{ 378 Directory: 1, 379 Name: "a", 380 Mode: 2, 381 Major: 3, 382 Minor: 4, 383 GID: 5, 384 }, 385 UID: 6, 386 }, 387 &Rumknod{ 388 Rmknod{QID: QID{Type: 1}}, 389 }, 390 &Tsetattrclunk{ 391 FID: 1, 392 Valid: SetAttrMask{ 393 Permissions: true, 394 UID: true, 395 GID: true, 396 Size: true, 397 ATime: true, 398 MTime: true, 399 CTime: true, 400 ATimeNotSystemTime: true, 401 MTimeNotSystemTime: true, 402 }, 403 SetAttr: SetAttr{ 404 Permissions: 1, 405 UID: 2, 406 GID: 3, 407 Size: 4, 408 ATimeSeconds: 5, 409 ATimeNanoSeconds: 6, 410 MTimeSeconds: 7, 411 MTimeNanoSeconds: 8, 412 }, 413 }, 414 } 415 416 for _, enc := range objs { 417 // Encode the original. 418 data := make([]byte, initialBufferLength) 419 buf := buffer{data: data[:0]} 420 enc.encode(&buf) 421 422 // Create a new object, same as the first. 423 enc2 := reflect.New(reflect.ValueOf(enc).Elem().Type()).Interface().(encoder) 424 buf2 := buffer{data: buf.data} 425 426 // To be fair, we need to add any payloads (directly). 427 if pl, ok := enc.(payloader); ok { 428 enc2.(payloader).SetPayload(pl.Payload()) 429 } 430 431 // And any file payloads (directly). 432 if fl, ok := enc.(filer); ok { 433 enc2.(filer).SetFilePayload(fl.FilePayload()) 434 } 435 436 // Mark sure it was okay. 437 enc2.decode(&buf2) 438 if buf2.isOverrun() { 439 t.Errorf("object %#v->%#v got overrun on decode", enc, enc2) 440 continue 441 } 442 443 // Check that they are equal. 444 if !reflect.DeepEqual(enc, enc2) { 445 t.Errorf("object %#v and %#v differ", enc, enc2) 446 continue 447 } 448 } 449 } 450 451 func TestMessageStrings(t *testing.T) { 452 for typ := range msgRegistry.factories { 453 entry := &msgRegistry.factories[typ] 454 if entry.create != nil { 455 name := fmt.Sprintf("%+v", typ) 456 t.Run(name, func(t *testing.T) { 457 defer func() { // Ensure no panic. 458 if r := recover(); r != nil { 459 t.Errorf("printing %s failed: %v", name, r) 460 } 461 }() 462 m := entry.create() 463 _ = fmt.Sprintf("%v", m) 464 err := ErrInvalidMsgType{MsgType(typ)} 465 _ = err.Error() 466 }) 467 } 468 } 469 } 470 471 func TestRegisterDuplicate(t *testing.T) { 472 defer func() { 473 if r := recover(); r == nil { 474 // We expect a panic. 475 t.FailNow() 476 } 477 }() 478 479 // Register a duplicate. 480 msgRegistry.register(MsgRlerror, func() message { return &Rlerror{} }) 481 } 482 483 func TestMsgCache(t *testing.T) { 484 // Cache starts empty. 485 if got, want := len(msgRegistry.factories[MsgRlerror].cache), 0; got != want { 486 t.Errorf("Wrong cache size, got: %d, want: %d", got, want) 487 } 488 489 // Message can be created with an empty cache. 490 msg, err := msgRegistry.get(0, MsgRlerror) 491 if err != nil { 492 t.Errorf("msgRegistry.get(): %v", err) 493 } 494 if got, want := len(msgRegistry.factories[MsgRlerror].cache), 0; got != want { 495 t.Errorf("Wrong cache size, got: %d, want: %d", got, want) 496 } 497 498 // Check that message is added to the cache when returned. 499 msgRegistry.put(msg) 500 if got, want := len(msgRegistry.factories[MsgRlerror].cache), 1; got != want { 501 t.Errorf("Wrong cache size, got: %d, want: %d", got, want) 502 } 503 504 // Check that returned message is reused. 505 if got, err := msgRegistry.get(0, MsgRlerror); err != nil { 506 t.Errorf("msgRegistry.get(): %v", err) 507 } else if msg != got { 508 t.Errorf("Message not reused, got: %d, want: %d", got, msg) 509 } 510 511 // Check that cache doesn't grow beyond max size. 512 for i := 0; i < maxCacheSize+1; i++ { 513 msgRegistry.put(&Rlerror{}) 514 } 515 if got, want := len(msgRegistry.factories[MsgRlerror].cache), maxCacheSize; got != want { 516 t.Errorf("Wrong cache size, got: %d, want: %d", got, want) 517 } 518 }