]>
Commit | Line | Data |
---|---|---|
1 | #include "cache.h" | |
2 | #include "blob.h" | |
3 | #include "dir.h" | |
4 | ||
5 | static void create_directories(const char *path, const struct checkout *state) | |
6 | { | |
7 | int len = strlen(path); | |
8 | char *buf = xmalloc(len + 1); | |
9 | const char *slash = path; | |
10 | ||
11 | while ((slash = strchr(slash+1, '/')) != NULL) { | |
12 | len = slash - path; | |
13 | memcpy(buf, path, len); | |
14 | buf[len] = 0; | |
15 | ||
16 | /* | |
17 | * For 'checkout-index --prefix=<dir>', <dir> is | |
18 | * allowed to be a symlink to an existing directory, | |
19 | * and we set 'state->base_dir_len' below, such that | |
20 | * we test the path components of the prefix with the | |
21 | * stat() function instead of the lstat() function. | |
22 | */ | |
23 | if (has_dirs_only_path(len, buf, state->base_dir_len)) | |
24 | continue; /* ok, it is already a directory. */ | |
25 | ||
26 | /* | |
27 | * If this mkdir() would fail, it could be that there | |
28 | * is already a symlink or something else exists | |
29 | * there, therefore we then try to unlink it and try | |
30 | * one more time to create the directory. | |
31 | */ | |
32 | if (mkdir(buf, 0777)) { | |
33 | if (errno == EEXIST && state->force && | |
34 | !unlink(buf) && !mkdir(buf, 0777)) | |
35 | continue; | |
36 | die("cannot create directory at %s", buf); | |
37 | } | |
38 | } | |
39 | free(buf); | |
40 | } | |
41 | ||
42 | static void remove_subtree(const char *path) | |
43 | { | |
44 | DIR *dir = opendir(path); | |
45 | struct dirent *de; | |
46 | char pathbuf[PATH_MAX]; | |
47 | char *name; | |
48 | ||
49 | if (!dir) | |
50 | die("cannot opendir %s (%s)", path, strerror(errno)); | |
51 | strcpy(pathbuf, path); | |
52 | name = pathbuf + strlen(path); | |
53 | *name++ = '/'; | |
54 | while ((de = readdir(dir)) != NULL) { | |
55 | struct stat st; | |
56 | if (is_dot_or_dotdot(de->d_name)) | |
57 | continue; | |
58 | strcpy(name, de->d_name); | |
59 | if (lstat(pathbuf, &st)) | |
60 | die("cannot lstat %s (%s)", pathbuf, strerror(errno)); | |
61 | if (S_ISDIR(st.st_mode)) | |
62 | remove_subtree(pathbuf); | |
63 | else if (unlink(pathbuf)) | |
64 | die("cannot unlink %s (%s)", pathbuf, strerror(errno)); | |
65 | } | |
66 | closedir(dir); | |
67 | if (rmdir(path)) | |
68 | die("cannot rmdir %s (%s)", path, strerror(errno)); | |
69 | } | |
70 | ||
71 | static int create_file(const char *path, unsigned int mode) | |
72 | { | |
73 | mode = (mode & 0100) ? 0777 : 0666; | |
74 | return open(path, O_WRONLY | O_CREAT | O_EXCL, mode); | |
75 | } | |
76 | ||
77 | static void *read_blob_entry(struct cache_entry *ce, const char *path, unsigned long *size) | |
78 | { | |
79 | enum object_type type; | |
80 | void *new = read_sha1_file(ce->sha1, &type, size); | |
81 | ||
82 | if (new) { | |
83 | if (type == OBJ_BLOB) | |
84 | return new; | |
85 | free(new); | |
86 | } | |
87 | return NULL; | |
88 | } | |
89 | ||
90 | static int write_entry(struct cache_entry *ce, char *path, const struct checkout *state, int to_tempfile) | |
91 | { | |
92 | int fd; | |
93 | long wrote; | |
94 | ||
95 | switch (ce->ce_mode & S_IFMT) { | |
96 | char *new; | |
97 | struct strbuf buf; | |
98 | unsigned long size; | |
99 | ||
100 | case S_IFREG: | |
101 | new = read_blob_entry(ce, path, &size); | |
102 | if (!new) | |
103 | return error("git checkout-index: unable to read sha1 file of %s (%s)", | |
104 | path, sha1_to_hex(ce->sha1)); | |
105 | ||
106 | /* | |
107 | * Convert from git internal format to working tree format | |
108 | */ | |
109 | strbuf_init(&buf, 0); | |
110 | if (convert_to_working_tree(ce->name, new, size, &buf)) { | |
111 | size_t newsize = 0; | |
112 | free(new); | |
113 | new = strbuf_detach(&buf, &newsize); | |
114 | size = newsize; | |
115 | } | |
116 | ||
117 | if (to_tempfile) { | |
118 | strcpy(path, ".merge_file_XXXXXX"); | |
119 | fd = mkstemp(path); | |
120 | } else | |
121 | fd = create_file(path, ce->ce_mode); | |
122 | if (fd < 0) { | |
123 | free(new); | |
124 | return error("git checkout-index: unable to create file %s (%s)", | |
125 | path, strerror(errno)); | |
126 | } | |
127 | ||
128 | wrote = write_in_full(fd, new, size); | |
129 | close(fd); | |
130 | free(new); | |
131 | if (wrote != size) | |
132 | return error("git checkout-index: unable to write file %s", path); | |
133 | break; | |
134 | case S_IFLNK: | |
135 | new = read_blob_entry(ce, path, &size); | |
136 | if (!new) | |
137 | return error("git checkout-index: unable to read sha1 file of %s (%s)", | |
138 | path, sha1_to_hex(ce->sha1)); | |
139 | if (to_tempfile || !has_symlinks) { | |
140 | if (to_tempfile) { | |
141 | strcpy(path, ".merge_link_XXXXXX"); | |
142 | fd = mkstemp(path); | |
143 | } else | |
144 | fd = create_file(path, 0666); | |
145 | if (fd < 0) { | |
146 | free(new); | |
147 | return error("git checkout-index: unable to create " | |
148 | "file %s (%s)", path, strerror(errno)); | |
149 | } | |
150 | wrote = write_in_full(fd, new, size); | |
151 | close(fd); | |
152 | free(new); | |
153 | if (wrote != size) | |
154 | return error("git checkout-index: unable to write file %s", | |
155 | path); | |
156 | } else { | |
157 | wrote = symlink(new, path); | |
158 | free(new); | |
159 | if (wrote) | |
160 | return error("git checkout-index: unable to create " | |
161 | "symlink %s (%s)", path, strerror(errno)); | |
162 | } | |
163 | break; | |
164 | case S_IFGITLINK: | |
165 | if (to_tempfile) | |
166 | return error("git checkout-index: cannot create temporary subproject %s", path); | |
167 | if (mkdir(path, 0777) < 0) | |
168 | return error("git checkout-index: cannot create subproject directory %s", path); | |
169 | break; | |
170 | default: | |
171 | return error("git checkout-index: unknown file mode for %s", path); | |
172 | } | |
173 | ||
174 | if (state->refresh_cache) { | |
175 | struct stat st; | |
176 | lstat(ce->name, &st); | |
177 | fill_stat_cache_info(ce, &st); | |
178 | } | |
179 | return 0; | |
180 | } | |
181 | ||
182 | int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *topath) | |
183 | { | |
184 | static char path[PATH_MAX + 1]; | |
185 | struct stat st; | |
186 | int len = state->base_dir_len; | |
187 | ||
188 | if (topath) | |
189 | return write_entry(ce, topath, state, 1); | |
190 | ||
191 | memcpy(path, state->base_dir, len); | |
192 | strcpy(path + len, ce->name); | |
193 | ||
194 | if (!lstat(path, &st)) { | |
195 | unsigned changed = ce_match_stat(ce, &st, CE_MATCH_IGNORE_VALID); | |
196 | if (!changed) | |
197 | return 0; | |
198 | if (!state->force) { | |
199 | if (!state->quiet) | |
200 | fprintf(stderr, "git-checkout-index: %s already exists\n", path); | |
201 | return -1; | |
202 | } | |
203 | ||
204 | /* | |
205 | * We unlink the old file, to get the new one with the | |
206 | * right permissions (including umask, which is nasty | |
207 | * to emulate by hand - much easier to let the system | |
208 | * just do the right thing) | |
209 | */ | |
210 | if (S_ISDIR(st.st_mode)) { | |
211 | /* If it is a gitlink, leave it alone! */ | |
212 | if (S_ISGITLINK(ce->ce_mode)) | |
213 | return 0; | |
214 | if (!state->force) | |
215 | return error("%s is a directory", path); | |
216 | remove_subtree(path); | |
217 | } else if (unlink(path)) | |
218 | return error("unable to unlink old '%s' (%s)", path, strerror(errno)); | |
219 | } else if (state->not_new) | |
220 | return 0; | |
221 | create_directories(path, state); | |
222 | return write_entry(ce, path, state, 0); | |
223 | } |