github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/libkb/db.go (about) 1 // Copyright 2015 Keybase, Inc. All rights reserved. Use of 2 // this source code is governed by the included BSD license. 3 4 package libkb 5 6 import ( 7 "encoding/json" 8 "fmt" 9 "regexp" 10 "strconv" 11 "strings" 12 13 "github.com/keybase/client/go/msgpack" 14 keybase1 "github.com/keybase/client/go/protocol/keybase1" 15 jsonw "github.com/keybase/go-jsonw" 16 ) 17 18 const ( 19 DBUser = 0x00 20 DBSig = 0x0f 21 DBTeamChain = 0x10 22 DBUserPlusAllKeysV1 = 0x19 23 24 DBChatArchiveRegistry = 0xa3 25 DBIncomingSharePreference = 0xa4 26 DBChatUserEmojis = 0xa5 27 DBChatInboxIndex = 0xa6 28 DBChatInboxConvs = 0xa7 29 DBChatParticipants = 0xa8 30 DBOpenTeams = 0xa9 31 DBNetworkInstrumentation = 0xaa 32 DBFeaturedBots = 0xab 33 DBChatEphemeralTracker = 0xac 34 DBLoginTimes = 0xad 35 DBChatJourney = 0xae 36 DBTeamRoleMap = 0xaf 37 DBMisc = 0xb0 38 DBTeamMerkleCheck = 0xb1 39 DBUidToServiceMap = 0xb2 40 DBChatPinIgnore = 0xb3 41 DBTeambotKey = 0xb4 42 DBTeambotKeyWrongKID = 0xb5 43 DBChatBotCommands = 0xb6 44 DBSavedContacts = 0xb7 45 DBChatLocation = 0xb8 46 DBHiddenChainStorage = 0xb9 47 DBContactResolution = 0xba 48 DBBoxAuditorPermanent = 0xbb 49 DBBoxAuditor = 0xbc 50 DBUserPlusKeysVersionedUnstubbed = 0xbd 51 DBOfflineRPC = 0xbe 52 DBChatCollapses = 0xbf 53 DBSupportsHiddenFlagStorage = 0xc0 54 DBMerkleAudit = 0xca 55 DBUnfurler = 0xcb 56 DBStellarDisclaimer = 0xcc 57 DBFTLStorage = 0xcd 58 DBTeamAuditor = 0xce 59 DBAttachmentUploader = 0xcf 60 DBLegacyHasRandomPW = 0xd0 61 DBDiskLRUEntries = 0xda 62 DBDiskLRUIndex = 0xdb 63 DBImplicitTeamConflictInfo = 0xdc 64 DBUidToFullName = 0xdd 65 DBUidToUsername = 0xde 66 DBUserPlusKeysVersioned = 0xdf 67 DBLink = 0xe0 68 DBLocalTrack = 0xe1 69 DBPGPKey = 0xe3 70 DBSigHints = 0xe4 71 DBProofCheck = 0xe5 72 DBUserSecretKeys = 0xe6 73 DBSigChainTailPublic = 0xe7 74 DBSigChainTailSemiprivate = 0xe8 75 DBSigChainTailEncrypted = 0xe9 76 DBChatActive = 0xea 77 DBUserEKBox = 0xeb 78 DBTeamEKBox = 0xec 79 DBChatIndex = 0xed 80 DBChatGiphy = 0xee 81 DBChatReacji = 0xef 82 DBMerkleRoot = 0xf0 83 DBTrackers = 0xf1 84 DBGregor = 0xf2 85 DBUnverifiedTrackersFollowers = 0xf3 86 DBUnverifiedTrackersFollowing = 0xf4 87 DBNotificationDismiss = 0xf5 88 DBChatBlockIndex = 0xf6 89 DBChatBlocks = 0xf7 90 DBChatOutbox = 0xf8 91 DBChatInbox = 0xf9 92 DBIdentify = 0xfa 93 DBResolveUsernameToUID = 0xfb 94 DBChatBodyHashIndex = 0xfc 95 DBMerkleStore = 0xfd 96 DBChatConvFailures = 0xfe 97 DBTeamList = 0xff 98 ) 99 100 // Note(maxtaco) 2018.10.08 --- Note a bug here, that we used the `libkb.DBChatInbox` type here. 101 // That's a copy-paste bug, but we get away with it since we have a `tid:` prefix that 102 // disambiguates these entries from true Chat entries. We're not going to fix it now 103 // since it would kill the team cache, but sometime in the future we should fix it. 104 const ( 105 DBSlowTeamsAlias = DBChatInbox 106 ) 107 108 const ( 109 DBLookupUsername = 0x00 110 // was once used to store latest merkle root with Key:"HEAD" 111 DBLookupMerkleRoot = 0x01 112 ) 113 114 func DbKeyUID(t ObjType, uid keybase1.UID) DbKey { 115 return DbKey{Typ: t, Key: uid.String()} 116 } 117 118 func DbKeyNotificationDismiss(prefix string, username NormalizedUsername) DbKey { 119 return DbKey{ 120 Typ: DBNotificationDismiss, 121 Key: fmt.Sprintf("%s:%s", prefix, username), 122 } 123 } 124 125 // IsPermDbKey returns true for keys ignored by the leveldb cleaner and always 126 // persisted to disk. Ideally these keys handling some cleanup/size bounding 127 // themselves. 128 func IsPermDbKey(typ ObjType) bool { 129 switch typ { 130 case DBDiskLRUEntries, 131 DBDiskLRUIndex, 132 DBOfflineRPC, 133 DBChatCollapses, 134 DBLegacyHasRandomPW, 135 DBChatReacji, 136 DBStellarDisclaimer, 137 DBChatIndex, 138 DBBoxAuditorPermanent, 139 DBSavedContacts, 140 DBContactResolution, 141 DBTeambotKeyWrongKID, 142 DBMisc, 143 DBIncomingSharePreference: 144 return true 145 default: 146 return false 147 } 148 } 149 150 type ObjType byte 151 152 type DbKey struct { 153 Typ ObjType 154 Key string 155 } 156 157 // tablePrefix builds a key prefix for the given table for use in `util.Range` 158 // or `util.BytesPrefix` 159 func tablePrefix(table string) []byte { 160 return []byte(fmt.Sprintf("%s:", table)) 161 } 162 163 func prefixStringWithTable(table string, typ ObjType) string { 164 return fmt.Sprintf("%s:%02x", table, typ) 165 } 166 167 func PrefixString(typ ObjType) string { 168 return prefixStringWithTable(typ.table(), typ) 169 } 170 171 func (t ObjType) table() string { 172 if IsPermDbKey(t) { 173 return levelDbTablePerm 174 } 175 return levelDbTableKv 176 } 177 178 func (k DbKey) toBytes(prefixString string) []byte { 179 return []byte(fmt.Sprintf("%s:%s", prefixString, k.Key)) 180 } 181 182 func (k DbKey) ToBytes() []byte { 183 return k.toBytes(PrefixString(k.Typ)) 184 } 185 186 func (k DbKey) ToBytesLookup() []byte { 187 return k.toBytes(prefixStringWithTable(levelDbTableLo, k.Typ)) 188 } 189 190 var fieldExp = regexp.MustCompile(`[a-f0-9]{2}`) 191 192 func DbKeyParse(s string) (string, DbKey, error) { 193 v := strings.Split(s, ":") 194 if len(v) < 3 { 195 return "", DbKey{}, fmt.Errorf("expected 3 colon-separated fields, found %d", len(v)) 196 } 197 198 if !fieldExp.MatchString(v[1]) { 199 return "", DbKey{}, fmt.Errorf("2nd field should be a 1-byte hex string") 200 } 201 202 b, err := strconv.ParseUint(v[1], 16, 8) 203 if err != nil { 204 return "", DbKey{}, err 205 } 206 dbKey := DbKey{ 207 Typ: ObjType(b), 208 Key: strings.Join(v[2:], ":"), 209 } 210 return v[0], dbKey, nil 211 } 212 213 func jsonLocalDbPut(ops LocalDbOps, id DbKey, aliases []DbKey, val *jsonw.Wrapper) error { 214 bytes, err := val.Marshal() 215 if err == nil { 216 err = ops.Put(id, aliases, bytes) 217 } 218 return err 219 } 220 func jsonLocalDbGet(ops LocalDbOps, id DbKey) (*jsonw.Wrapper, error) { 221 bytes, found, err := ops.Get(id) 222 var ret *jsonw.Wrapper 223 if found { 224 ret, err = jsonw.Unmarshal(bytes) 225 } 226 return ret, err 227 } 228 229 func jsonLocalDbGetInto(ops LocalDbOps, obj interface{}, id DbKey) (found bool, err error) { 230 var buf []byte 231 buf, found, err = ops.Get(id) 232 if err == nil && found { 233 err = jsonw.EnsureMaxDepthBytesDefault(buf) 234 if err != nil { 235 return found, err 236 } 237 err = json.Unmarshal(buf, &obj) 238 } 239 return found, err 240 } 241 242 func jsonLocalDbPutObj(ops LocalDbOps, id DbKey, aliases []DbKey, obj interface{}) (err error) { 243 var bytes []byte 244 bytes, err = json.Marshal(obj) 245 if err == nil { 246 err = ops.Put(id, aliases, bytes) 247 } 248 return err 249 } 250 251 func jsonLocalDbLookup(ops LocalDbOps, id DbKey) (*jsonw.Wrapper, error) { 252 bytes, found, err := ops.Lookup(id) 253 var ret *jsonw.Wrapper 254 if found { 255 ret, err = jsonw.Unmarshal(bytes) 256 } 257 return ret, err 258 } 259 260 func jsonLocalDbLookupIntoMsgpack(ops LocalDbOps, obj interface{}, alias DbKey) (found bool, err error) { 261 var buf []byte 262 buf, found, err = ops.Lookup(alias) 263 if err != nil || !found { 264 return found, err 265 } 266 err = msgpack.Decode(obj, buf) 267 return true, err 268 } 269 270 func jsonLocalDbGetIntoMsgpack(ops LocalDbOps, obj interface{}, id DbKey) (found bool, err error) { 271 var buf []byte 272 buf, found, err = ops.Get(id) 273 if err != nil || !found { 274 return found, err 275 } 276 err = msgpack.Decode(obj, buf) 277 return true, err 278 } 279 280 func jsonLocalDbPutObjMsgpack(ops LocalDbOps, id DbKey, aliases []DbKey, obj interface{}) error { 281 bytes, err := msgpack.Encode(obj) 282 if err != nil { 283 return err 284 } 285 return ops.Put(id, aliases, bytes) 286 } 287 288 type JSONLocalDb struct { 289 engine LocalDb 290 } 291 292 func NewJSONLocalDb(e LocalDb) *JSONLocalDb { return &JSONLocalDb{e} } 293 func (j *JSONLocalDb) Open() error { return j.engine.Open() } 294 func (j *JSONLocalDb) ForceOpen() error { return j.engine.ForceOpen() } 295 func (j *JSONLocalDb) Close() error { return j.engine.Close() } 296 func (j *JSONLocalDb) Nuke() (string, error) { return j.engine.Nuke() } 297 func (j *JSONLocalDb) Clean(force bool) error { return j.engine.Clean(force) } 298 func (j *JSONLocalDb) Stats() string { return j.engine.Stats() } 299 func (j *JSONLocalDb) CompactionStats() (bool, bool, error) { 300 return j.engine.CompactionStats() 301 } 302 func (j *JSONLocalDb) KeysWithPrefixes(prefixes ...[]byte) (DBKeySet, error) { 303 return j.engine.KeysWithPrefixes(prefixes...) 304 } 305 306 func (j *JSONLocalDb) PutRaw(id DbKey, b []byte) error { return j.engine.Put(id, nil, b) } 307 func (j *JSONLocalDb) GetRaw(id DbKey) ([]byte, bool, error) { return j.engine.Get(id) } 308 func (j *JSONLocalDb) Delete(id DbKey) error { return j.engine.Delete(id) } 309 310 func (j *JSONLocalDb) Put(id DbKey, aliases []DbKey, val *jsonw.Wrapper) error { 311 return jsonLocalDbPut(j.engine, id, aliases, val) 312 } 313 314 func (j *JSONLocalDb) Get(id DbKey) (*jsonw.Wrapper, error) { 315 return jsonLocalDbGet(j.engine, id) 316 } 317 318 func (j *JSONLocalDb) GetInto(obj interface{}, id DbKey) (found bool, err error) { 319 return jsonLocalDbGetInto(j.engine, obj, id) 320 } 321 322 func (j *JSONLocalDb) PutObj(id DbKey, aliases []DbKey, obj interface{}) (err error) { 323 return jsonLocalDbPutObj(j.engine, id, aliases, obj) 324 } 325 326 func (j *JSONLocalDb) Lookup(id DbKey) (*jsonw.Wrapper, error) { 327 return jsonLocalDbLookup(j.engine, id) 328 } 329 330 func (j *JSONLocalDb) LookupIntoMsgpack(obj interface{}, alias DbKey) (found bool, err error) { 331 return jsonLocalDbLookupIntoMsgpack(j.engine, obj, alias) 332 } 333 334 func (j *JSONLocalDb) GetIntoMsgpack(obj interface{}, id DbKey) (found bool, err error) { 335 return jsonLocalDbGetIntoMsgpack(j.engine, obj, id) 336 } 337 338 func (j *JSONLocalDb) PutObjMsgpack(id DbKey, aliases []DbKey, obj interface{}) (err error) { 339 return jsonLocalDbPutObjMsgpack(j.engine, id, aliases, obj) 340 } 341 342 func (j *JSONLocalDb) OpenTransaction() (JSONLocalDbTransaction, error) { 343 var ( 344 jtr JSONLocalDbTransaction 345 err error 346 ) 347 if jtr.tr, err = j.engine.OpenTransaction(); err != nil { 348 return JSONLocalDbTransaction{}, err 349 } 350 return jtr, nil 351 } 352 353 func (j *JSONLocalDb) GetEngine() LocalDb { 354 return j.engine 355 } 356 357 type JSONLocalDbTransaction struct { 358 tr LocalDbTransaction 359 } 360 361 func (j JSONLocalDbTransaction) PutRaw(id DbKey, b []byte) error { return j.tr.Put(id, nil, b) } 362 func (j JSONLocalDbTransaction) GetRaw(id DbKey) ([]byte, bool, error) { return j.tr.Get(id) } 363 func (j JSONLocalDbTransaction) Delete(id DbKey) error { return j.tr.Delete(id) } 364 365 func (j JSONLocalDbTransaction) Put(id DbKey, aliases []DbKey, val *jsonw.Wrapper) error { 366 return jsonLocalDbPut(j.tr, id, aliases, val) 367 } 368 369 func (j JSONLocalDbTransaction) Get(id DbKey) (*jsonw.Wrapper, error) { 370 return jsonLocalDbGet(j.tr, id) 371 } 372 373 func (j JSONLocalDbTransaction) GetInto(obj interface{}, id DbKey) (found bool, err error) { 374 return jsonLocalDbGetInto(j.tr, obj, id) 375 } 376 377 func (j JSONLocalDbTransaction) PutObj(id DbKey, aliases []DbKey, obj interface{}) (err error) { 378 return jsonLocalDbPutObj(j.tr, id, aliases, obj) 379 } 380 381 func (j JSONLocalDbTransaction) Lookup(id DbKey) (*jsonw.Wrapper, error) { 382 return jsonLocalDbLookup(j.tr, id) 383 } 384 385 func (j JSONLocalDbTransaction) Commit() error { 386 return j.tr.Commit() 387 } 388 389 func (j JSONLocalDbTransaction) Discard() { 390 j.tr.Discard() 391 }