-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathobject.c
More file actions
187 lines (155 loc) · 4.71 KB
/
object.c
File metadata and controls
187 lines (155 loc) · 4.71 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
#include "mvcs.h"
#include <openssl/sha.h>
/* Compute SHA-256 hash of data */
int compute_hash(const void *data, size_t len, unsigned char *hash) {
SHA256_CTX ctx;
SHA256_Init(&ctx);
SHA256_Update(&ctx, data, len);
SHA256_Final(hash, &ctx);
return 0;
}
/* Convert hash to hexadecimal string */
void hash_to_hex(const unsigned char *hash, char *hex) {
for (int i = 0; i < HASH_SIZE; i++) {
sprintf(hex + i * 2, "%02x", hash[i]);
}
hex[HASH_HEX_SIZE - 1] = '\0';
}
/* Convert hexadecimal string to hash */
int hex_to_hash(const char *hex, unsigned char *hash) {
if (strlen(hex) != HASH_HEX_SIZE - 1) {
return -1;
}
for (int i = 0; i < HASH_SIZE; i++) {
sscanf(hex + i * 2, "%2hhx", &hash[i]);
}
return 0;
}
/* Get object file path from hash */
char *get_object_path(const unsigned char *hash, char *path) {
char hex[HASH_HEX_SIZE];
hash_to_hex(hash, hex);
/* Object stored as .mvcs/objects/ab/cdef... (first 2 chars as dir) */
sprintf(path, "%s/%.2s/%s", OBJECTS_DIR, hex, hex + 2);
return path;
}
/* Write object to filesystem */
int write_object(int type, const void *data, size_t len, unsigned char *hash) {
/* Create header: "type size\0" */
char header[64];
const char *type_str;
switch (type) {
case OBJ_BLOB: type_str = "blob"; break;
case OBJ_TREE: type_str = "tree"; break;
case OBJ_COMMIT: type_str = "commit"; break;
default: return -1;
}
int header_len = sprintf(header, "%s %zu", type_str, len) + 1;
/* Compute hash of header + data */
unsigned char *full_data = malloc(header_len + len);
if (!full_data) return -1;
memcpy(full_data, header, header_len);
memcpy(full_data + header_len, data, len);
compute_hash(full_data, header_len + len, hash);
/* Get object path and create directory */
char path[MAX_PATH_LEN];
get_object_path(hash, path);
/* Create parent directory */
char dir[MAX_PATH_LEN];
char hex[HASH_HEX_SIZE];
hash_to_hex(hash, hex);
sprintf(dir, "%s/%.2s", OBJECTS_DIR, hex);
create_directory(dir);
/* Write object file */
FILE *f = fopen(path, "wb");
if (!f) {
free(full_data);
return -1;
}
fwrite(full_data, 1, header_len + len, f);
fclose(f);
free(full_data);
return 0;
}
/* Read object from filesystem */
int read_object(const unsigned char *hash, int *type, void **data, size_t *len) {
char path[MAX_PATH_LEN];
get_object_path(hash, path);
FILE *f = fopen(path, "rb");
if (!f) return -1;
/* Read entire file */
fseek(f, 0, SEEK_END);
long file_size = ftell(f);
fseek(f, 0, SEEK_SET);
unsigned char *full_data = malloc(file_size);
if (!full_data) {
fclose(f);
return -1;
}
if (fread(full_data, 1, file_size, f) != (size_t)file_size) {
free(full_data);
fclose(f);
return -1;
}
fclose(f);
/* Parse header */
char type_str[16];
size_t obj_len;
int header_len = 0;
if (sscanf((char *)full_data, "%15s %zu%n", type_str, &obj_len, &header_len) != 2) {
free(full_data);
return -1;
}
header_len++; /* Skip null terminator */
/* Determine type */
if (strcmp(type_str, "blob") == 0) *type = OBJ_BLOB;
else if (strcmp(type_str, "tree") == 0) *type = OBJ_TREE;
else if (strcmp(type_str, "commit") == 0) *type = OBJ_COMMIT;
else {
free(full_data);
return -1;
}
/* Extract data */
*len = obj_len;
/* Allocate one extra byte for null terminator (useful for text objects) */
*data = malloc(*len + 1);
if (!*data) {
free(full_data);
return -1;
}
memcpy(*data, full_data + header_len, *len);
/* Null-terminate for safety when treating as string */
((char *)(*data))[*len] = '\0';
free(full_data);
return 0;
}
/* Hash a file and return its hash */
int hash_file(const char *path, unsigned char *hash) {
FILE *f = fopen(path, "rb");
if (!f) return -1;
fseek(f, 0, SEEK_END);
long size = ftell(f);
fseek(f, 0, SEEK_SET);
if (size > MAX_CONTENT_SIZE) {
fclose(f);
return -1;
}
unsigned char *content = malloc(size);
if (!content) {
fclose(f);
return -1;
}
if (fread(content, 1, size, f) != (size_t)size) {
free(content);
fclose(f);
return -1;
}
fclose(f);
int ret = write_object(OBJ_BLOB, content, size, hash);
free(content);
return ret;
}
/* Write blob object from file */
int write_blob(const char *path, unsigned char *hash) {
return hash_file(path, hash);
}