github.com/cyverse/go-irodsclient@v0.13.2/irods/fs/collection.go (about) 1 package fs 2 3 import ( 4 "encoding/binary" 5 "fmt" 6 "strconv" 7 "time" 8 9 "github.com/cyverse/go-irodsclient/irods/common" 10 "github.com/cyverse/go-irodsclient/irods/connection" 11 "github.com/cyverse/go-irodsclient/irods/message" 12 "github.com/cyverse/go-irodsclient/irods/types" 13 "github.com/cyverse/go-irodsclient/irods/util" 14 "golang.org/x/xerrors" 15 ) 16 17 /* 18 Table "public.r_coll_main" 19 Column | Type | Collation | Nullable | Default 20 ------------------+-------------------------+-----------+----------+------------------------ 21 coll_id | bigint | | not null | 22 parent_coll_name | character varying(2700) | | not null | 23 coll_name | character varying(2700) | | not null | 24 coll_owner_name | character varying(250) | | not null | 25 coll_owner_zone | character varying(250) | | not null | 26 coll_map_id | bigint | | | 0 27 coll_inheritance | character varying(1000) | | | 28 coll_type | character varying(250) | | | '0'::character varying 29 coll_info1 | character varying(2700) | | | '0'::character varying 30 coll_info2 | character varying(2700) | | | '0'::character varying 31 coll_expiry_ts | character varying(32) | | | 32 r_comment | character varying(1000) | | | 33 create_ts | character varying(32) | | | 34 modify_ts | character varying(32) | | | 35 Indexes: 36 "idx_coll_main2" UNIQUE, btree (parent_coll_name, coll_name) 37 "idx_coll_main3" UNIQUE, btree (coll_name) 38 "idx_coll_main1" btree (coll_id) 39 "idx_coll_main_parent_coll_name" btree (parent_coll_name) 40 */ 41 42 // GetCollection returns a collection for the path 43 func GetCollection(conn *connection.IRODSConnection, path string) (*types.IRODSCollection, error) { 44 if conn == nil || !conn.IsConnected() { 45 return nil, xerrors.Errorf("connection is nil or disconnected") 46 } 47 48 metrics := conn.GetMetrics() 49 if metrics != nil { 50 metrics.IncreaseCounterForStat(1) 51 } 52 53 // lock the connection 54 conn.Lock() 55 defer conn.Unlock() 56 57 query := message.NewIRODSMessageQueryRequest(common.MaxQueryRows, 0, 0, 0) 58 query.AddSelect(common.ICAT_COLUMN_COLL_ID, 1) 59 query.AddSelect(common.ICAT_COLUMN_COLL_NAME, 1) 60 query.AddSelect(common.ICAT_COLUMN_COLL_OWNER_NAME, 1) 61 query.AddSelect(common.ICAT_COLUMN_COLL_CREATE_TIME, 1) 62 query.AddSelect(common.ICAT_COLUMN_COLL_MODIFY_TIME, 1) 63 64 condVal := fmt.Sprintf("= '%s'", path) 65 query.AddCondition(common.ICAT_COLUMN_COLL_NAME, condVal) 66 67 queryResult := message.IRODSMessageQueryResponse{} 68 err := conn.Request(query, &queryResult, nil) 69 if err != nil { 70 return nil, xerrors.Errorf("failed to receive collection query result message: %w", err) 71 } 72 73 err = queryResult.CheckError() 74 if err != nil { 75 if types.GetIRODSErrorCode(err) == common.CAT_NO_ROWS_FOUND { 76 return nil, xerrors.Errorf("failed to find the collection for path %s: %w", path, types.NewFileNotFoundError(path)) 77 } 78 return nil, xerrors.Errorf("received collection query error: %w", err) 79 } 80 81 if queryResult.RowCount != 1 { 82 // file not found 83 return nil, xerrors.Errorf("failed to find the collection for path %s: %w", path, types.NewFileNotFoundError(path)) 84 } 85 86 if queryResult.AttributeCount > len(queryResult.SQLResult) { 87 return nil, xerrors.Errorf("failed to receive collection attributes - requires %d, but received %d attributes", queryResult.AttributeCount, len(queryResult.SQLResult)) 88 } 89 90 var collectionID int64 = -1 91 collectionPath := "" 92 collectionOwner := "" 93 createTime := time.Time{} 94 modifyTime := time.Time{} 95 for idx := 0; idx < queryResult.AttributeCount; idx++ { 96 sqlResult := queryResult.SQLResult[idx] 97 if len(sqlResult.Values) != queryResult.RowCount { 98 return nil, xerrors.Errorf("failed to receive collection rows - requires %d, but received %d attributes", queryResult.RowCount, len(sqlResult.Values)) 99 } 100 101 value := sqlResult.Values[0] 102 103 switch sqlResult.AttributeIndex { 104 case int(common.ICAT_COLUMN_COLL_ID): 105 cID, err := strconv.ParseInt(value, 10, 64) 106 if err != nil { 107 return nil, xerrors.Errorf("failed to parse collection id '%s': %w", value, err) 108 } 109 collectionID = cID 110 case int(common.ICAT_COLUMN_COLL_NAME): 111 collectionPath = value 112 case int(common.ICAT_COLUMN_COLL_OWNER_NAME): 113 collectionOwner = value 114 case int(common.ICAT_COLUMN_COLL_CREATE_TIME): 115 cT, err := util.GetIRODSDateTime(value) 116 if err != nil { 117 return nil, xerrors.Errorf("failed to parse create time '%s': %w", value, err) 118 } 119 createTime = cT 120 case int(common.ICAT_COLUMN_COLL_MODIFY_TIME): 121 mT, err := util.GetIRODSDateTime(value) 122 if err != nil { 123 return nil, xerrors.Errorf("failed to parse modify time '%s': %w", value, err) 124 } 125 modifyTime = mT 126 default: 127 // ignore 128 } 129 } 130 131 if collectionID == -1 { 132 return nil, xerrors.Errorf("failed to find the collection for path %s: %w", path, types.NewFileNotFoundError(path)) 133 } 134 135 return &types.IRODSCollection{ 136 ID: collectionID, 137 Path: collectionPath, 138 Name: util.GetIRODSPathFileName(collectionPath), 139 Owner: collectionOwner, 140 CreateTime: createTime, 141 ModifyTime: modifyTime, 142 }, nil 143 } 144 145 // ListCollectionMeta returns a colleciton metadata for the path 146 func ListCollectionMeta(conn *connection.IRODSConnection, path string) ([]*types.IRODSMeta, error) { 147 if conn == nil || !conn.IsConnected() { 148 return nil, xerrors.Errorf("connection is nil or disconnected") 149 } 150 151 metrics := conn.GetMetrics() 152 if metrics != nil { 153 metrics.IncreaseCounterForMetadataList(1) 154 } 155 156 // lock the connection 157 conn.Lock() 158 defer conn.Unlock() 159 160 metas := []*types.IRODSMeta{} 161 162 continueQuery := true 163 continueIndex := 0 164 for continueQuery { 165 query := message.NewIRODSMessageQueryRequest(common.MaxQueryRows, continueIndex, 0, 0) 166 query.AddSelect(common.ICAT_COLUMN_META_COLL_ATTR_ID, 1) 167 query.AddSelect(common.ICAT_COLUMN_META_COLL_ATTR_NAME, 1) 168 query.AddSelect(common.ICAT_COLUMN_META_COLL_ATTR_VALUE, 1) 169 query.AddSelect(common.ICAT_COLUMN_META_COLL_ATTR_UNITS, 1) 170 171 condVal := fmt.Sprintf("= '%s'", path) 172 query.AddCondition(common.ICAT_COLUMN_COLL_NAME, condVal) 173 174 queryResult := message.IRODSMessageQueryResponse{} 175 err := conn.Request(query, &queryResult, nil) 176 if err != nil { 177 return nil, xerrors.Errorf("failed to receive a collection metadata query result message: %w", err) 178 } 179 180 err = queryResult.CheckError() 181 if err != nil { 182 if types.GetIRODSErrorCode(err) == common.CAT_NO_ROWS_FOUND { 183 // empty 184 break 185 } 186 return nil, xerrors.Errorf("received collection metadata query error: %w", err) 187 } 188 189 if queryResult.RowCount == 0 { 190 break 191 } 192 193 if queryResult.AttributeCount > len(queryResult.SQLResult) { 194 return nil, xerrors.Errorf("failed to receive collection metadata attributes - requires %d, but received %d attributes", queryResult.AttributeCount, len(queryResult.SQLResult)) 195 } 196 197 pagenatedMetas := make([]*types.IRODSMeta, queryResult.RowCount) 198 199 for attr := 0; attr < queryResult.AttributeCount; attr++ { 200 sqlResult := queryResult.SQLResult[attr] 201 if len(sqlResult.Values) != queryResult.RowCount { 202 return nil, xerrors.Errorf("failed to receive collection metadata rows - requires %d, but received %d attributes", queryResult.RowCount, len(sqlResult.Values)) 203 } 204 205 for row := 0; row < queryResult.RowCount; row++ { 206 value := sqlResult.Values[row] 207 208 if pagenatedMetas[row] == nil { 209 // create a new 210 pagenatedMetas[row] = &types.IRODSMeta{ 211 AVUID: -1, 212 Name: "", 213 Value: "", 214 Units: "", 215 } 216 } 217 218 switch sqlResult.AttributeIndex { 219 case int(common.ICAT_COLUMN_META_COLL_ATTR_ID): 220 avuID, err := strconv.ParseInt(value, 10, 64) 221 if err != nil { 222 return nil, xerrors.Errorf("failed to parse collection metadata id '%s': %w", value, err) 223 } 224 pagenatedMetas[row].AVUID = avuID 225 case int(common.ICAT_COLUMN_META_COLL_ATTR_NAME): 226 pagenatedMetas[row].Name = value 227 case int(common.ICAT_COLUMN_META_COLL_ATTR_VALUE): 228 pagenatedMetas[row].Value = value 229 case int(common.ICAT_COLUMN_META_COLL_ATTR_UNITS): 230 pagenatedMetas[row].Units = value 231 default: 232 // ignore 233 } 234 } 235 } 236 237 metas = append(metas, pagenatedMetas...) 238 239 continueIndex = queryResult.ContinueIndex 240 if continueIndex == 0 { 241 continueQuery = false 242 } 243 } 244 245 return metas, nil 246 } 247 248 // ListCollectionAccesses returns collection accesses for the path 249 func ListCollectionAccesses(conn *connection.IRODSConnection, path string) ([]*types.IRODSAccess, error) { 250 if conn == nil || !conn.IsConnected() { 251 return nil, xerrors.Errorf("connection is nil or disconnected") 252 } 253 254 metrics := conn.GetMetrics() 255 if metrics != nil { 256 metrics.IncreaseCounterForAccessList(1) 257 } 258 259 // lock the connection 260 conn.Lock() 261 defer conn.Unlock() 262 263 accesses := []*types.IRODSAccess{} 264 265 continueQuery := true 266 continueIndex := 0 267 for continueQuery { 268 query := message.NewIRODSMessageQueryRequest(common.MaxQueryRows, continueIndex, 0, 0) 269 query.AddSelect(common.ICAT_COLUMN_COLL_ACCESS_NAME, 1) 270 query.AddSelect(common.ICAT_COLUMN_USER_NAME, 1) 271 query.AddSelect(common.ICAT_COLUMN_USER_ZONE, 1) 272 query.AddSelect(common.ICAT_COLUMN_USER_TYPE, 1) 273 274 condVal := fmt.Sprintf("= '%s'", path) 275 query.AddCondition(common.ICAT_COLUMN_COLL_NAME, condVal) 276 277 queryResult := message.IRODSMessageQueryResponse{} 278 err := conn.Request(query, &queryResult, nil) 279 if err != nil { 280 return nil, xerrors.Errorf("failed to receive a collection access query result message: %w", err) 281 } 282 283 err = queryResult.CheckError() 284 if err != nil { 285 if types.GetIRODSErrorCode(err) == common.CAT_NO_ROWS_FOUND { 286 // empty 287 break 288 } 289 return nil, xerrors.Errorf("received collection access query error: %w", err) 290 } 291 292 if queryResult.RowCount == 0 { 293 break 294 } 295 296 if queryResult.AttributeCount > len(queryResult.SQLResult) { 297 return nil, xerrors.Errorf("failed to receive collection access attributes - requires %d, but received %d attributes", queryResult.AttributeCount, len(queryResult.SQLResult)) 298 } 299 300 pagenatedAccesses := make([]*types.IRODSAccess, queryResult.RowCount) 301 302 for attr := 0; attr < queryResult.AttributeCount; attr++ { 303 sqlResult := queryResult.SQLResult[attr] 304 if len(sqlResult.Values) != queryResult.RowCount { 305 return nil, xerrors.Errorf("failed to receive collection access rows - requires %d, but received %d attributes", queryResult.RowCount, len(sqlResult.Values)) 306 } 307 308 for row := 0; row < queryResult.RowCount; row++ { 309 value := sqlResult.Values[row] 310 311 if pagenatedAccesses[row] == nil { 312 // create a new 313 pagenatedAccesses[row] = &types.IRODSAccess{ 314 Path: path, 315 UserName: "", 316 UserZone: "", 317 AccessLevel: types.IRODSAccessLevelNone, 318 UserType: types.IRODSUserRodsUser, 319 } 320 } 321 322 switch sqlResult.AttributeIndex { 323 case int(common.ICAT_COLUMN_COLL_ACCESS_NAME): 324 pagenatedAccesses[row].AccessLevel = types.IRODSAccessLevelType(value) 325 case int(common.ICAT_COLUMN_USER_TYPE): 326 pagenatedAccesses[row].UserType = types.IRODSUserType(value) 327 case int(common.ICAT_COLUMN_USER_NAME): 328 pagenatedAccesses[row].UserName = value 329 case int(common.ICAT_COLUMN_USER_ZONE): 330 pagenatedAccesses[row].UserZone = value 331 default: 332 // ignore 333 } 334 } 335 } 336 337 accesses = append(accesses, pagenatedAccesses...) 338 339 continueIndex = queryResult.ContinueIndex 340 if continueIndex == 0 { 341 continueQuery = false 342 } 343 } 344 345 return accesses, nil 346 } 347 348 // ListAccessesForSubCollections returns collection accesses for subcollections in the given path 349 func ListAccessesForSubCollections(conn *connection.IRODSConnection, path string) ([]*types.IRODSAccess, error) { 350 if conn == nil || !conn.IsConnected() { 351 return nil, xerrors.Errorf("connection is nil or disconnected") 352 } 353 354 metrics := conn.GetMetrics() 355 if metrics != nil { 356 metrics.IncreaseCounterForAccessList(1) 357 } 358 359 // lock the connection 360 conn.Lock() 361 defer conn.Unlock() 362 363 accesses := []*types.IRODSAccess{} 364 365 continueQuery := true 366 continueIndex := 0 367 for continueQuery { 368 query := message.NewIRODSMessageQueryRequest(common.MaxQueryRows, continueIndex, 0, 0) 369 query.AddSelect(common.ICAT_COLUMN_COLL_NAME, 1) 370 query.AddSelect(common.ICAT_COLUMN_COLL_ACCESS_NAME, 1) 371 query.AddSelect(common.ICAT_COLUMN_USER_NAME, 1) 372 query.AddSelect(common.ICAT_COLUMN_USER_ZONE, 1) 373 query.AddSelect(common.ICAT_COLUMN_USER_TYPE, 1) 374 375 condVal := fmt.Sprintf("= '%s'", path) 376 query.AddCondition(common.ICAT_COLUMN_COLL_PARENT_NAME, condVal) 377 378 queryResult := message.IRODSMessageQueryResponse{} 379 err := conn.Request(query, &queryResult, nil) 380 if err != nil { 381 return nil, xerrors.Errorf("failed to receive a collection access query result message: %w", err) 382 } 383 384 err = queryResult.CheckError() 385 if err != nil { 386 if types.GetIRODSErrorCode(err) == common.CAT_NO_ROWS_FOUND { 387 // empty 388 break 389 } 390 return nil, xerrors.Errorf("received collection access query error: %w", err) 391 } 392 393 if queryResult.RowCount == 0 { 394 break 395 } 396 397 if queryResult.AttributeCount > len(queryResult.SQLResult) { 398 return nil, xerrors.Errorf("failed to receive collection access attributes - requires %d, but received %d attributes", queryResult.AttributeCount, len(queryResult.SQLResult)) 399 } 400 401 pagenatedAccesses := make([]*types.IRODSAccess, queryResult.RowCount) 402 403 for attr := 0; attr < queryResult.AttributeCount; attr++ { 404 sqlResult := queryResult.SQLResult[attr] 405 if len(sqlResult.Values) != queryResult.RowCount { 406 return nil, xerrors.Errorf("failed to receive collection access rows - requires %d, but received %d attributes", queryResult.RowCount, len(sqlResult.Values)) 407 } 408 409 for row := 0; row < queryResult.RowCount; row++ { 410 value := sqlResult.Values[row] 411 412 if pagenatedAccesses[row] == nil { 413 // create a new 414 pagenatedAccesses[row] = &types.IRODSAccess{ 415 Path: "", 416 UserName: "", 417 UserZone: "", 418 AccessLevel: types.IRODSAccessLevelNone, 419 UserType: types.IRODSUserRodsUser, 420 } 421 } 422 423 switch sqlResult.AttributeIndex { 424 case int(common.ICAT_COLUMN_COLL_NAME): 425 pagenatedAccesses[row].Path = value 426 case int(common.ICAT_COLUMN_COLL_ACCESS_NAME): 427 pagenatedAccesses[row].AccessLevel = types.IRODSAccessLevelType(value) 428 case int(common.ICAT_COLUMN_USER_TYPE): 429 pagenatedAccesses[row].UserType = types.IRODSUserType(value) 430 case int(common.ICAT_COLUMN_USER_NAME): 431 pagenatedAccesses[row].UserName = value 432 case int(common.ICAT_COLUMN_USER_ZONE): 433 pagenatedAccesses[row].UserZone = value 434 default: 435 // ignore 436 } 437 } 438 } 439 440 accesses = append(accesses, pagenatedAccesses...) 441 442 continueIndex = queryResult.ContinueIndex 443 if continueIndex == 0 { 444 continueQuery = false 445 } 446 } 447 448 return accesses, nil 449 } 450 451 // ListSubCollections lists subcollections in the given collection 452 func ListSubCollections(conn *connection.IRODSConnection, path string) ([]*types.IRODSCollection, error) { 453 if conn == nil || !conn.IsConnected() { 454 return nil, xerrors.Errorf("connection is nil or disconnected") 455 } 456 457 metrics := conn.GetMetrics() 458 if metrics != nil { 459 metrics.IncreaseCounterForList(1) 460 } 461 462 // lock the connection 463 conn.Lock() 464 defer conn.Unlock() 465 466 collections := []*types.IRODSCollection{} 467 468 continueQuery := true 469 continueIndex := 0 470 for continueQuery { 471 query := message.NewIRODSMessageQueryRequest(common.MaxQueryRows, continueIndex, 0, 0) 472 query.AddSelect(common.ICAT_COLUMN_COLL_ID, 1) 473 query.AddSelect(common.ICAT_COLUMN_COLL_NAME, 1) 474 query.AddSelect(common.ICAT_COLUMN_COLL_OWNER_NAME, 1) 475 query.AddSelect(common.ICAT_COLUMN_COLL_CREATE_TIME, 1) 476 query.AddSelect(common.ICAT_COLUMN_COLL_MODIFY_TIME, 1) 477 478 condVal := fmt.Sprintf("= '%s'", path) 479 query.AddCondition(common.ICAT_COLUMN_COLL_PARENT_NAME, condVal) 480 481 queryResult := message.IRODSMessageQueryResponse{} 482 err := conn.Request(query, &queryResult, nil) 483 if err != nil { 484 return nil, xerrors.Errorf("failed to receive a collection query result message: %w", err) 485 } 486 487 err = queryResult.CheckError() 488 if err != nil { 489 if types.GetIRODSErrorCode(err) == common.CAT_NO_ROWS_FOUND { 490 // empty 491 break 492 } 493 return nil, xerrors.Errorf("received collection query error: %w", err) 494 } 495 496 if queryResult.RowCount == 0 { 497 break 498 } 499 500 if queryResult.AttributeCount > len(queryResult.SQLResult) { 501 return nil, xerrors.Errorf("failed to receive collection attributes - requires %d, but received %d attributes", queryResult.AttributeCount, len(queryResult.SQLResult)) 502 } 503 504 pagenatedCollections := make([]*types.IRODSCollection, queryResult.RowCount) 505 506 for attr := 0; attr < queryResult.AttributeCount; attr++ { 507 sqlResult := queryResult.SQLResult[attr] 508 if len(sqlResult.Values) != queryResult.RowCount { 509 return nil, xerrors.Errorf("failed to receive collection rows - requires %d, but received %d attributes", queryResult.RowCount, len(sqlResult.Values)) 510 } 511 512 for row := 0; row < queryResult.RowCount; row++ { 513 value := sqlResult.Values[row] 514 515 if pagenatedCollections[row] == nil { 516 // create a new 517 pagenatedCollections[row] = &types.IRODSCollection{ 518 ID: -1, 519 Path: "", 520 Name: "", 521 Owner: "", 522 CreateTime: time.Time{}, 523 ModifyTime: time.Time{}, 524 } 525 } 526 527 switch sqlResult.AttributeIndex { 528 case int(common.ICAT_COLUMN_COLL_ID): 529 cID, err := strconv.ParseInt(value, 10, 64) 530 if err != nil { 531 return nil, xerrors.Errorf("failed to parse collection id '%s': %w", value, err) 532 } 533 pagenatedCollections[row].ID = cID 534 case int(common.ICAT_COLUMN_COLL_NAME): 535 pagenatedCollections[row].Path = value 536 pagenatedCollections[row].Name = util.GetIRODSPathFileName(value) 537 case int(common.ICAT_COLUMN_COLL_OWNER_NAME): 538 pagenatedCollections[row].Owner = value 539 case int(common.ICAT_COLUMN_COLL_CREATE_TIME): 540 cT, err := util.GetIRODSDateTime(value) 541 if err != nil { 542 return nil, xerrors.Errorf("failed to parse create time '%s': %w", value, err) 543 } 544 pagenatedCollections[row].CreateTime = cT 545 case int(common.ICAT_COLUMN_COLL_MODIFY_TIME): 546 mT, err := util.GetIRODSDateTime(value) 547 if err != nil { 548 return nil, xerrors.Errorf("failed to parse modify time '%s': %w", value, err) 549 } 550 pagenatedCollections[row].ModifyTime = mT 551 default: 552 // ignore 553 } 554 } 555 } 556 557 collections = append(collections, pagenatedCollections...) 558 559 continueIndex = queryResult.ContinueIndex 560 if continueIndex == 0 { 561 continueQuery = false 562 } 563 } 564 565 return collections, nil 566 } 567 568 // CreateCollection creates a collection for the path 569 func CreateCollection(conn *connection.IRODSConnection, path string, recurse bool) error { 570 if conn == nil || !conn.IsConnected() { 571 return xerrors.Errorf("connection is nil or disconnected") 572 } 573 574 metrics := conn.GetMetrics() 575 if metrics != nil { 576 metrics.IncreaseCounterForCollectionCreate(1) 577 } 578 579 // lock the connection 580 conn.Lock() 581 defer conn.Unlock() 582 583 request := message.NewIRODSMessageMakeCollectionRequest(path, recurse) 584 response := message.IRODSMessageMakeCollectionResponse{} 585 err := conn.RequestAndCheck(request, &response, nil) 586 if err != nil { 587 return xerrors.Errorf("received create collection error: %w", err) 588 } 589 return nil 590 } 591 592 // DeleteCollection deletes a collection for the path 593 func DeleteCollection(conn *connection.IRODSConnection, path string, recurse bool, force bool) error { 594 if conn == nil || !conn.IsConnected() { 595 return xerrors.Errorf("connection is nil or disconnected") 596 } 597 598 metrics := conn.GetMetrics() 599 if metrics != nil { 600 metrics.IncreaseCounterForCollectionDelete(1) 601 } 602 603 // lock the connection 604 conn.Lock() 605 defer conn.Unlock() 606 607 request := message.NewIRODSMessageRemoveCollectionRequest(path, recurse, force) 608 response := message.IRODSMessageRemoveCollectionResponse{} 609 err := conn.RequestAndCheck(request, &response, nil) 610 if err != nil { 611 if types.GetIRODSErrorCode(err) == common.CAT_NO_ROWS_FOUND { 612 return xerrors.Errorf("failed to find the collection for path %s: %w", path, types.NewFileNotFoundError(path)) 613 } else if types.GetIRODSErrorCode(err) == common.CAT_COLLECTION_NOT_EMPTY { 614 return xerrors.Errorf("the collection for path %s is empty: %w", path, types.NewCollectionNotEmptyError(path)) 615 } 616 617 return xerrors.Errorf("received delete collection error: %w", err) 618 } 619 620 for response.Result == int(common.SYS_SVR_TO_CLI_COLL_STAT) { 621 // pack length - Big Endian 622 replyBuffer := make([]byte, 4) 623 binary.BigEndian.PutUint32(replyBuffer, uint32(common.SYS_CLI_TO_SVR_COLL_STAT_REPLY)) 624 625 err = conn.Send(replyBuffer, 4) 626 if err != nil { 627 return xerrors.Errorf("failed to reply to a collection deletion response message: %w", err) 628 } 629 630 responseMessageReply, err := conn.ReadMessage(nil) 631 if err != nil { 632 return xerrors.Errorf("failed to receive a collection deletion response message: %w", err) 633 } 634 635 response.FromMessage(responseMessageReply) 636 } 637 638 return nil 639 } 640 641 // MoveCollection moves a collection for the path to another path 642 func MoveCollection(conn *connection.IRODSConnection, srcPath string, destPath string) error { 643 if conn == nil || !conn.IsConnected() { 644 return xerrors.Errorf("connection is nil or disconnected") 645 } 646 647 metrics := conn.GetMetrics() 648 if metrics != nil { 649 metrics.IncreaseCounterForCollectionRename(1) 650 } 651 652 // lock the connection 653 conn.Lock() 654 defer conn.Unlock() 655 656 request := message.NewIRODSMessageMoveCollectionRequest(srcPath, destPath) 657 response := message.IRODSMessageMoveCollectionResponse{} 658 err := conn.RequestAndCheck(request, &response, nil) 659 if err != nil { 660 if types.GetIRODSErrorCode(err) == common.CAT_NO_ROWS_FOUND { 661 return xerrors.Errorf("failed to find the collection for path %s: %w", srcPath, types.NewFileNotFoundError(srcPath)) 662 } 663 return xerrors.Errorf("received move collection error: %w", err) 664 } 665 return nil 666 } 667 668 // AddCollectionMeta sets metadata of a data object for the path to the given key values. 669 // metadata.AVUID is ignored 670 func AddCollectionMeta(conn *connection.IRODSConnection, path string, metadata *types.IRODSMeta) error { 671 if conn == nil || !conn.IsConnected() { 672 return xerrors.Errorf("connection is nil or disconnected") 673 } 674 675 metrics := conn.GetMetrics() 676 if metrics != nil { 677 metrics.IncreaseCounterForMetadataCreate(1) 678 } 679 680 // lock the connection 681 conn.Lock() 682 defer conn.Unlock() 683 684 request := message.NewIRODSMessageAddMetadataRequest(types.IRODSCollectionMetaItemType, path, metadata) 685 response := message.IRODSMessageModifyMetadataResponse{} 686 err := conn.RequestAndCheck(request, &response, nil) 687 if err != nil { 688 return xerrors.Errorf("received add collection meta error: %w", err) 689 } 690 return nil 691 } 692 693 // DeleteCollectionMeta sets metadata of a data object for the path to the given key values. 694 // The metadata AVU is selected on basis of AVUID if it is supplied, otherwise on basis of Name, Value and Units. 695 func DeleteCollectionMeta(conn *connection.IRODSConnection, path string, metadata *types.IRODSMeta) error { 696 if conn == nil || !conn.IsConnected() { 697 return xerrors.Errorf("connection is nil or disconnected") 698 } 699 700 metrics := conn.GetMetrics() 701 if metrics != nil { 702 metrics.IncreaseCounterForMetadataDelete(1) 703 } 704 705 // lock the connection 706 conn.Lock() 707 defer conn.Unlock() 708 709 var request *message.IRODSMessageModifyMetadataRequest 710 711 if metadata.AVUID != 0 { 712 request = message.NewIRODSMessageRemoveMetadataByIDRequest(types.IRODSCollectionMetaItemType, path, metadata.AVUID) 713 } else if metadata.Units == "" && metadata.Value == "" { 714 request = message.NewIRODSMessageRemoveMetadataWildcardRequest(types.IRODSCollectionMetaItemType, path, metadata.Name) 715 } else { 716 request = message.NewIRODSMessageRemoveMetadataRequest(types.IRODSCollectionMetaItemType, path, metadata) 717 } 718 719 response := message.IRODSMessageModifyMetadataResponse{} 720 err := conn.RequestAndCheck(request, &response, nil) 721 if err != nil { 722 if types.GetIRODSErrorCode(err) == common.CAT_NO_ROWS_FOUND { 723 return xerrors.Errorf("failed to find the collection for path %s: %w", path, types.NewFileNotFoundError(path)) 724 } 725 return xerrors.Errorf("received delete collection meta error: %w", err) 726 } 727 return nil 728 } 729 730 // SearchCollectionsByMeta searches collections by metadata 731 func SearchCollectionsByMeta(conn *connection.IRODSConnection, metaName string, metaValue string) ([]*types.IRODSCollection, error) { 732 if conn == nil || !conn.IsConnected() { 733 return nil, xerrors.Errorf("connection is nil or disconnected") 734 } 735 736 metrics := conn.GetMetrics() 737 if metrics != nil { 738 metrics.IncreaseCounterForSearch(1) 739 } 740 741 // lock the connection 742 conn.Lock() 743 defer conn.Unlock() 744 745 collections := []*types.IRODSCollection{} 746 747 continueQuery := true 748 continueIndex := 0 749 for continueQuery { 750 query := message.NewIRODSMessageQueryRequest(common.MaxQueryRows, continueIndex, 0, 0) 751 query.AddSelect(common.ICAT_COLUMN_COLL_ID, 1) 752 query.AddSelect(common.ICAT_COLUMN_COLL_NAME, 1) 753 query.AddSelect(common.ICAT_COLUMN_COLL_OWNER_NAME, 1) 754 query.AddSelect(common.ICAT_COLUMN_COLL_CREATE_TIME, 1) 755 query.AddSelect(common.ICAT_COLUMN_COLL_MODIFY_TIME, 1) 756 757 metaNameCondVal := fmt.Sprintf("= '%s'", metaName) 758 query.AddCondition(common.ICAT_COLUMN_META_COLL_ATTR_NAME, metaNameCondVal) 759 metaValueCondVal := fmt.Sprintf("= '%s'", metaValue) 760 query.AddCondition(common.ICAT_COLUMN_META_COLL_ATTR_VALUE, metaValueCondVal) 761 762 queryResult := message.IRODSMessageQueryResponse{} 763 err := conn.Request(query, &queryResult, nil) 764 if err != nil { 765 return nil, xerrors.Errorf("failed to receive a collection query result message: %w", err) 766 } 767 768 err = queryResult.CheckError() 769 if err != nil { 770 if types.GetIRODSErrorCode(err) == common.CAT_NO_ROWS_FOUND { 771 // empty 772 break 773 } 774 return nil, xerrors.Errorf("received collection query error: %w", err) 775 } 776 777 if queryResult.RowCount == 0 { 778 break 779 } 780 781 if queryResult.AttributeCount > len(queryResult.SQLResult) { 782 return nil, xerrors.Errorf("failed to receive collection attributes - requires %d, but received %d attributes", queryResult.AttributeCount, len(queryResult.SQLResult)) 783 } 784 785 pagenatedCollections := make([]*types.IRODSCollection, queryResult.RowCount) 786 787 for attr := 0; attr < queryResult.AttributeCount; attr++ { 788 sqlResult := queryResult.SQLResult[attr] 789 if len(sqlResult.Values) != queryResult.RowCount { 790 return nil, xerrors.Errorf("failed to receive collection rows - requires %d, but received %d attributes", queryResult.RowCount, len(sqlResult.Values)) 791 } 792 793 for row := 0; row < queryResult.RowCount; row++ { 794 value := sqlResult.Values[row] 795 796 if pagenatedCollections[row] == nil { 797 // create a new 798 pagenatedCollections[row] = &types.IRODSCollection{ 799 ID: -1, 800 Path: "", 801 Name: "", 802 Owner: "", 803 CreateTime: time.Time{}, 804 ModifyTime: time.Time{}, 805 } 806 } 807 808 switch sqlResult.AttributeIndex { 809 case int(common.ICAT_COLUMN_COLL_ID): 810 cID, err := strconv.ParseInt(value, 10, 64) 811 if err != nil { 812 return nil, xerrors.Errorf("failed to parse collection id '%s': %w", value, err) 813 } 814 pagenatedCollections[row].ID = cID 815 case int(common.ICAT_COLUMN_COLL_NAME): 816 pagenatedCollections[row].Path = value 817 pagenatedCollections[row].Name = util.GetIRODSPathFileName(value) 818 case int(common.ICAT_COLUMN_COLL_OWNER_NAME): 819 pagenatedCollections[row].Owner = value 820 case int(common.ICAT_COLUMN_COLL_CREATE_TIME): 821 cT, err := util.GetIRODSDateTime(value) 822 if err != nil { 823 return nil, xerrors.Errorf("failed to parse create time '%s': %w", value, err) 824 } 825 pagenatedCollections[row].CreateTime = cT 826 case int(common.ICAT_COLUMN_COLL_MODIFY_TIME): 827 mT, err := util.GetIRODSDateTime(value) 828 if err != nil { 829 return nil, xerrors.Errorf("failed to parse modify time '%s': %w", value, err) 830 } 831 pagenatedCollections[row].ModifyTime = mT 832 default: 833 // ignore 834 } 835 } 836 } 837 838 collections = append(collections, pagenatedCollections...) 839 840 continueIndex = queryResult.ContinueIndex 841 if continueIndex == 0 { 842 continueQuery = false 843 } 844 } 845 846 return collections, nil 847 } 848 849 // SearchCollectionsByMetaWildcard searches collections by metadata 850 // Caution: This is a very slow operation 851 func SearchCollectionsByMetaWildcard(conn *connection.IRODSConnection, metaName string, metaValue string) ([]*types.IRODSCollection, error) { 852 if conn == nil || !conn.IsConnected() { 853 return nil, xerrors.Errorf("connection is nil or disconnected") 854 } 855 856 metrics := conn.GetMetrics() 857 if metrics != nil { 858 metrics.IncreaseCounterForSearch(1) 859 } 860 861 // lock the connection 862 conn.Lock() 863 defer conn.Unlock() 864 865 collections := []*types.IRODSCollection{} 866 867 continueQuery := true 868 continueIndex := 0 869 for continueQuery { 870 query := message.NewIRODSMessageQueryRequest(common.MaxQueryRows, continueIndex, 0, 0) 871 query.AddSelect(common.ICAT_COLUMN_COLL_ID, 1) 872 query.AddSelect(common.ICAT_COLUMN_COLL_NAME, 1) 873 query.AddSelect(common.ICAT_COLUMN_COLL_OWNER_NAME, 1) 874 query.AddSelect(common.ICAT_COLUMN_COLL_CREATE_TIME, 1) 875 query.AddSelect(common.ICAT_COLUMN_COLL_MODIFY_TIME, 1) 876 877 metaNameCondVal := fmt.Sprintf("= '%s'", metaName) 878 query.AddCondition(common.ICAT_COLUMN_META_COLL_ATTR_NAME, metaNameCondVal) 879 metaValueCondVal := fmt.Sprintf("like '%s'", metaValue) 880 query.AddCondition(common.ICAT_COLUMN_META_COLL_ATTR_VALUE, metaValueCondVal) 881 882 queryResult := message.IRODSMessageQueryResponse{} 883 err := conn.Request(query, &queryResult, nil) 884 if err != nil { 885 return nil, xerrors.Errorf("failed to receive a collection query result message: %w", err) 886 } 887 888 err = queryResult.CheckError() 889 if err != nil { 890 if types.GetIRODSErrorCode(err) == common.CAT_NO_ROWS_FOUND { 891 // empty 892 break 893 } 894 return nil, xerrors.Errorf("received collection query error: %w", err) 895 } 896 897 if queryResult.RowCount == 0 { 898 break 899 } 900 901 if queryResult.AttributeCount > len(queryResult.SQLResult) { 902 return nil, xerrors.Errorf("failed to receive collection attributes - requires %d, but received %d attributes", queryResult.AttributeCount, len(queryResult.SQLResult)) 903 } 904 905 pagenatedCollections := make([]*types.IRODSCollection, queryResult.RowCount) 906 907 for attr := 0; attr < queryResult.AttributeCount; attr++ { 908 sqlResult := queryResult.SQLResult[attr] 909 if len(sqlResult.Values) != queryResult.RowCount { 910 return nil, xerrors.Errorf("failed to receive data object rows - requires %d, but received %d attributes", queryResult.RowCount, len(sqlResult.Values)) 911 } 912 913 for attr := 0; attr < queryResult.AttributeCount; attr++ { 914 sqlResult := queryResult.SQLResult[attr] 915 if len(sqlResult.Values) != queryResult.RowCount { 916 return nil, xerrors.Errorf("failed to receive collection rows - requires %d, but received %d attributes", queryResult.RowCount, len(sqlResult.Values)) 917 } 918 919 for row := 0; row < queryResult.RowCount; row++ { 920 value := sqlResult.Values[row] 921 922 if pagenatedCollections[row] == nil { 923 // create a new 924 pagenatedCollections[row] = &types.IRODSCollection{ 925 ID: -1, 926 Path: "", 927 Name: "", 928 Owner: "", 929 CreateTime: time.Time{}, 930 ModifyTime: time.Time{}, 931 } 932 } 933 934 switch sqlResult.AttributeIndex { 935 case int(common.ICAT_COLUMN_COLL_ID): 936 cID, err := strconv.ParseInt(value, 10, 64) 937 if err != nil { 938 return nil, xerrors.Errorf("failed to parse collection id '%s': %w", value, err) 939 } 940 pagenatedCollections[row].ID = cID 941 case int(common.ICAT_COLUMN_COLL_NAME): 942 pagenatedCollections[row].Path = value 943 pagenatedCollections[row].Name = util.GetIRODSPathFileName(value) 944 case int(common.ICAT_COLUMN_COLL_OWNER_NAME): 945 pagenatedCollections[row].Owner = value 946 case int(common.ICAT_COLUMN_COLL_CREATE_TIME): 947 cT, err := util.GetIRODSDateTime(value) 948 if err != nil { 949 return nil, xerrors.Errorf("failed to parse create time '%s': %w", value, err) 950 } 951 pagenatedCollections[row].CreateTime = cT 952 case int(common.ICAT_COLUMN_COLL_MODIFY_TIME): 953 mT, err := util.GetIRODSDateTime(value) 954 if err != nil { 955 return nil, xerrors.Errorf("failed to parse modify time '%s': %w", value, err) 956 } 957 pagenatedCollections[row].ModifyTime = mT 958 default: 959 // ignore 960 } 961 } 962 } 963 } 964 965 collections = append(collections, pagenatedCollections...) 966 967 continueIndex = queryResult.ContinueIndex 968 if continueIndex == 0 { 969 continueQuery = false 970 } 971 } 972 973 return collections, nil 974 } 975 976 // ChangeCollectionAccess changes access on a collection. 977 func ChangeCollectionAccess(conn *connection.IRODSConnection, path string, access types.IRODSAccessLevelType, userName, zoneName string, recursive bool, adminFlag bool) error { 978 if conn == nil || !conn.IsConnected() { 979 return xerrors.Errorf("connection is nil or disconnected") 980 } 981 982 metrics := conn.GetMetrics() 983 if metrics != nil { 984 metrics.IncreaseCounterForAccessUpdate(1) 985 } 986 987 // lock the connection 988 conn.Lock() 989 defer conn.Unlock() 990 991 request := message.NewIRODSMessageModifyAccessRequest(access.ChmodString(), userName, zoneName, path, recursive, adminFlag) 992 response := message.IRODSMessageModifyAccessResponse{} 993 err := conn.RequestAndCheck(request, &response, nil) 994 if err != nil { 995 if types.GetIRODSErrorCode(err) == common.CAT_NO_ROWS_FOUND { 996 return xerrors.Errorf("failed to find the collection for path %s: %w", path, types.NewFileNotFoundError(path)) 997 } 998 return xerrors.Errorf("received change collection access error: %w", err) 999 } 1000 return nil 1001 } 1002 1003 // SetAccessInherit sets the inherit bit on a collection. 1004 func SetAccessInherit(conn *connection.IRODSConnection, path string, inherit, recursive, adminFlag bool) error { 1005 if conn == nil || !conn.IsConnected() { 1006 return xerrors.Errorf("connection is nil or disconnected") 1007 } 1008 1009 metrics := conn.GetMetrics() 1010 if metrics != nil { 1011 metrics.IncreaseCounterForAccessUpdate(1) 1012 } 1013 1014 // lock the connection 1015 conn.Lock() 1016 defer conn.Unlock() 1017 1018 inheritStr := "inherit" 1019 1020 if !inherit { 1021 inheritStr = "noinherit" 1022 } 1023 1024 request := message.NewIRODSMessageModifyAccessRequest(inheritStr, "", "", path, recursive, adminFlag) 1025 response := message.IRODSMessageModifyAccessResponse{} 1026 err := conn.RequestAndCheck(request, &response, nil) 1027 if err != nil { 1028 if types.GetIRODSErrorCode(err) == common.CAT_NO_ROWS_FOUND { 1029 return xerrors.Errorf("failed to find the collection for path %s: %w", path, types.NewFileNotFoundError(path)) 1030 } 1031 return xerrors.Errorf("received set access inherit error: %w", err) 1032 } 1033 return nil 1034 }