2020-11-05 04:45:16 +00:00
|
|
|
package mysql
|
|
|
|
|
|
|
|
import (
|
2021-09-14 12:11:07 +00:00
|
|
|
"context"
|
2020-11-05 04:45:16 +00:00
|
|
|
"database/sql"
|
|
|
|
"fmt"
|
|
|
|
"time"
|
|
|
|
|
2021-06-26 04:46:51 +00:00
|
|
|
"github.com/fleetdm/fleet/v4/server/fleet"
|
2020-12-16 17:16:55 +00:00
|
|
|
"github.com/jmoiron/sqlx"
|
2020-11-05 04:45:16 +00:00
|
|
|
"github.com/pkg/errors"
|
|
|
|
)
|
|
|
|
|
2021-09-14 12:11:07 +00:00
|
|
|
func (d *Datastore) NewCarve(ctx context.Context, metadata *fleet.CarveMetadata) (*fleet.CarveMetadata, error) {
|
2020-11-05 04:45:16 +00:00
|
|
|
stmt := `INSERT INTO carve_metadata (
|
|
|
|
host_id,
|
2020-12-16 17:16:55 +00:00
|
|
|
created_at,
|
2020-11-05 04:45:16 +00:00
|
|
|
name,
|
|
|
|
block_count,
|
|
|
|
block_size,
|
|
|
|
carve_size,
|
|
|
|
carve_id,
|
|
|
|
request_id,
|
|
|
|
session_id
|
|
|
|
) VALUES (
|
|
|
|
?,
|
|
|
|
?,
|
|
|
|
?,
|
|
|
|
?,
|
|
|
|
?,
|
|
|
|
?,
|
|
|
|
?,
|
2020-12-16 17:16:55 +00:00
|
|
|
?,
|
2020-11-05 04:45:16 +00:00
|
|
|
?
|
|
|
|
)`
|
|
|
|
|
2021-09-01 19:50:52 +00:00
|
|
|
result, err := d.writer.Exec(
|
2020-11-05 04:45:16 +00:00
|
|
|
stmt,
|
|
|
|
metadata.HostId,
|
2020-12-16 17:16:55 +00:00
|
|
|
metadata.CreatedAt.Format(mySQLTimestampFormat),
|
2020-11-05 04:45:16 +00:00
|
|
|
metadata.Name,
|
|
|
|
metadata.BlockCount,
|
|
|
|
metadata.BlockSize,
|
|
|
|
metadata.CarveSize,
|
|
|
|
metadata.CarveId,
|
|
|
|
metadata.RequestId,
|
|
|
|
metadata.SessionId,
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrap(err, "insert carve metadata")
|
|
|
|
}
|
|
|
|
|
|
|
|
id, _ := result.LastInsertId()
|
|
|
|
metadata.ID = id
|
|
|
|
|
|
|
|
return metadata, nil
|
|
|
|
}
|
|
|
|
|
2020-12-16 17:16:55 +00:00
|
|
|
// UpdateCarve updates the carve metadata in database
|
|
|
|
// Only max_block and expired are updatable
|
2021-09-14 12:11:07 +00:00
|
|
|
func (d *Datastore) UpdateCarve(ctx context.Context, metadata *fleet.CarveMetadata) error {
|
2021-09-08 18:43:22 +00:00
|
|
|
return updateCarveDB(d.writer, metadata)
|
|
|
|
}
|
|
|
|
|
|
|
|
func updateCarveDB(exec sqlx.Execer, metadata *fleet.CarveMetadata) error {
|
2020-12-16 17:16:55 +00:00
|
|
|
stmt := `
|
|
|
|
UPDATE carve_metadata SET
|
|
|
|
max_block = ?,
|
|
|
|
expired = ?
|
|
|
|
WHERE id = ?
|
|
|
|
`
|
2021-09-08 18:43:22 +00:00
|
|
|
_, err := exec.Exec(
|
2020-12-16 17:16:55 +00:00
|
|
|
stmt,
|
|
|
|
metadata.MaxBlock,
|
|
|
|
metadata.Expired,
|
|
|
|
metadata.ID,
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "update carve metadata")
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-09-14 12:11:07 +00:00
|
|
|
func (d *Datastore) CleanupCarves(ctx context.Context, now time.Time) (int, error) {
|
2020-11-05 04:45:16 +00:00
|
|
|
var countExpired int
|
|
|
|
err := d.withRetryTxx(func(tx *sqlx.Tx) error {
|
|
|
|
// Get IDs of carves to expire
|
|
|
|
stmt := `
|
|
|
|
SELECT id
|
|
|
|
FROM carve_metadata
|
|
|
|
WHERE expired = 0 AND created_at < (? - INTERVAL 24 HOUR)
|
|
|
|
LIMIT 50000
|
|
|
|
`
|
|
|
|
var expiredCarves []int64
|
|
|
|
if err := tx.Select(&expiredCarves, stmt, now); err != nil {
|
|
|
|
return errors.Wrap(err, "get expired carves")
|
|
|
|
}
|
|
|
|
|
|
|
|
countExpired = len(expiredCarves)
|
|
|
|
|
|
|
|
if len(expiredCarves) == 0 {
|
|
|
|
// Nothing to do
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Delete carve block data
|
|
|
|
stmt = `
|
|
|
|
DELETE FROM carve_blocks
|
|
|
|
WHERE metadata_id IN (?)
|
|
|
|
`
|
|
|
|
stmt, args, err := sqlx.In(stmt, expiredCarves)
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "IN for DELETE FROM carve_blocks")
|
|
|
|
}
|
|
|
|
stmt = tx.Rebind(stmt)
|
2021-08-13 17:59:31 +00:00
|
|
|
if _, err := tx.Exec(stmt, args...); err != nil {
|
2020-11-05 04:45:16 +00:00
|
|
|
return errors.Wrap(err, "delete carve blocks")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Mark metadata expired
|
|
|
|
stmt = `
|
|
|
|
UPDATE carve_metadata
|
|
|
|
SET expired = 1
|
|
|
|
WHERE id IN (?)
|
|
|
|
`
|
|
|
|
stmt, args, err = sqlx.In(stmt, expiredCarves)
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "IN for UPDATE carve_metadata")
|
|
|
|
}
|
|
|
|
stmt = tx.Rebind(stmt)
|
2021-08-13 17:59:31 +00:00
|
|
|
if _, err := tx.Exec(stmt, args...); err != nil {
|
2020-11-05 04:45:16 +00:00
|
|
|
return errors.Wrap(err, "update carve_metadtata")
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return countExpired, nil
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// Selecting max_block should be very efficient because MySQL is able to use
|
|
|
|
// the index metadata and optimizes away the SELECT.
|
|
|
|
const carveSelectFields = `
|
|
|
|
id,
|
|
|
|
host_id,
|
|
|
|
created_at,
|
|
|
|
name,
|
|
|
|
block_count,
|
|
|
|
block_size,
|
|
|
|
carve_size,
|
|
|
|
carve_id,
|
|
|
|
request_id,
|
|
|
|
session_id,
|
|
|
|
expired,
|
2020-12-16 17:16:55 +00:00
|
|
|
max_block
|
2020-11-05 04:45:16 +00:00
|
|
|
`
|
|
|
|
|
2021-09-14 12:11:07 +00:00
|
|
|
func (d *Datastore) Carve(ctx context.Context, carveId int64) (*fleet.CarveMetadata, error) {
|
2020-11-05 04:45:16 +00:00
|
|
|
stmt := fmt.Sprintf(`
|
|
|
|
SELECT %s
|
|
|
|
FROM carve_metadata
|
|
|
|
WHERE id = ?`,
|
|
|
|
carveSelectFields,
|
|
|
|
)
|
|
|
|
|
2021-06-06 22:07:29 +00:00
|
|
|
var metadata fleet.CarveMetadata
|
2021-09-01 19:50:52 +00:00
|
|
|
if err := d.reader.Get(&metadata, stmt, carveId); err != nil {
|
2020-11-05 04:45:16 +00:00
|
|
|
return nil, errors.Wrap(err, "get carve by ID")
|
|
|
|
}
|
|
|
|
|
|
|
|
return &metadata, nil
|
|
|
|
}
|
|
|
|
|
2021-09-14 12:11:07 +00:00
|
|
|
func (d *Datastore) CarveBySessionId(ctx context.Context, sessionId string) (*fleet.CarveMetadata, error) {
|
2020-11-05 04:45:16 +00:00
|
|
|
stmt := fmt.Sprintf(`
|
|
|
|
SELECT %s
|
|
|
|
FROM carve_metadata
|
|
|
|
WHERE session_id = ?`,
|
|
|
|
carveSelectFields,
|
|
|
|
)
|
|
|
|
|
2021-06-06 22:07:29 +00:00
|
|
|
var metadata fleet.CarveMetadata
|
2021-09-01 19:50:52 +00:00
|
|
|
if err := d.reader.Get(&metadata, stmt, sessionId); err != nil {
|
2020-11-05 04:45:16 +00:00
|
|
|
return nil, errors.Wrap(err, "get carve by session ID")
|
|
|
|
}
|
|
|
|
|
|
|
|
return &metadata, nil
|
|
|
|
}
|
|
|
|
|
2021-09-14 12:11:07 +00:00
|
|
|
func (d *Datastore) CarveByName(ctx context.Context, name string) (*fleet.CarveMetadata, error) {
|
2020-11-05 04:45:16 +00:00
|
|
|
stmt := fmt.Sprintf(`
|
|
|
|
SELECT %s
|
|
|
|
FROM carve_metadata
|
|
|
|
WHERE name = ?`,
|
|
|
|
carveSelectFields,
|
|
|
|
)
|
|
|
|
|
2021-06-06 22:07:29 +00:00
|
|
|
var metadata fleet.CarveMetadata
|
2021-09-01 19:50:52 +00:00
|
|
|
if err := d.reader.Get(&metadata, stmt, name); err != nil {
|
2020-11-05 04:45:16 +00:00
|
|
|
return nil, errors.Wrap(err, "get carve by name")
|
|
|
|
}
|
|
|
|
|
|
|
|
return &metadata, nil
|
|
|
|
}
|
|
|
|
|
2021-09-14 12:11:07 +00:00
|
|
|
func (d *Datastore) ListCarves(ctx context.Context, opt fleet.CarveListOptions) ([]*fleet.CarveMetadata, error) {
|
2020-11-05 04:45:16 +00:00
|
|
|
stmt := fmt.Sprintf(`
|
|
|
|
SELECT %s
|
|
|
|
FROM carve_metadata`,
|
|
|
|
carveSelectFields,
|
|
|
|
)
|
|
|
|
if !opt.Expired {
|
|
|
|
stmt += ` WHERE NOT expired `
|
|
|
|
}
|
|
|
|
stmt = appendListOptionsToSQL(stmt, opt.ListOptions)
|
2021-06-06 22:07:29 +00:00
|
|
|
carves := []*fleet.CarveMetadata{}
|
2021-09-01 19:50:52 +00:00
|
|
|
if err := d.reader.Select(&carves, stmt); err != nil && err != sql.ErrNoRows {
|
2020-11-05 04:45:16 +00:00
|
|
|
return nil, errors.Wrap(err, "list carves")
|
|
|
|
}
|
|
|
|
|
|
|
|
return carves, nil
|
|
|
|
}
|
|
|
|
|
2021-09-14 12:11:07 +00:00
|
|
|
func (d *Datastore) NewBlock(ctx context.Context, metadata *fleet.CarveMetadata, blockId int64, data []byte) error {
|
2021-09-08 18:43:22 +00:00
|
|
|
return d.withTx(func(tx *sqlx.Tx) error {
|
|
|
|
stmt := `
|
2020-11-05 04:45:16 +00:00
|
|
|
INSERT INTO carve_blocks (
|
|
|
|
metadata_id,
|
|
|
|
block_id,
|
|
|
|
data
|
|
|
|
) VALUES (
|
|
|
|
?,
|
|
|
|
?,
|
|
|
|
?
|
|
|
|
)`
|
2021-09-08 18:43:22 +00:00
|
|
|
if _, err := tx.Exec(stmt, metadata.ID, blockId, data); err != nil {
|
2020-12-16 17:16:55 +00:00
|
|
|
return errors.Wrap(err, "insert carve block")
|
|
|
|
}
|
|
|
|
|
2021-09-08 18:43:22 +00:00
|
|
|
if metadata.MaxBlock < blockId {
|
|
|
|
// Update max_block
|
|
|
|
metadata.MaxBlock = blockId
|
|
|
|
if err := updateCarveDB(tx, metadata); err != nil {
|
|
|
|
return errors.Wrap(err, "update carve max block")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
})
|
2020-11-05 04:45:16 +00:00
|
|
|
}
|
|
|
|
|
2021-09-14 12:11:07 +00:00
|
|
|
func (d *Datastore) GetBlock(ctx context.Context, metadata *fleet.CarveMetadata, blockId int64) ([]byte, error) {
|
2020-11-05 04:45:16 +00:00
|
|
|
stmt := `
|
|
|
|
SELECT data
|
|
|
|
FROM carve_blocks
|
|
|
|
WHERE metadata_id = ? AND block_id = ?
|
|
|
|
`
|
|
|
|
var data []byte
|
2021-09-01 19:50:52 +00:00
|
|
|
if err := d.reader.Get(&data, stmt, metadata.ID, blockId); err != nil {
|
2020-11-05 04:45:16 +00:00
|
|
|
return nil, errors.Wrap(err, "select data")
|
|
|
|
}
|
|
|
|
|
|
|
|
return data, nil
|
|
|
|
}
|