Mercurial > illumos > onarm
annotate usr/src/cmd/fs.d/nfs/nfslog/dbtab.c @ 4:1a15d5aaf794
synchronized with onnv_86 (6202) in onnv-gate
author | Koji Uno <koji.uno@sun.com> |
---|---|
date | Mon, 31 Aug 2009 14:38:03 +0900 |
parents | c9caec207d52 |
children |
rev | line source |
---|---|
0 | 1 /* |
2 * CDDL HEADER START | |
3 * | |
4 * The contents of this file are subject to the terms of the | |
5 * Common Development and Distribution License (the "License"). | |
6 * You may not use this file except in compliance with the License. | |
7 * | |
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE | |
9 * or http://www.opensolaris.org/os/licensing. | |
10 * See the License for the specific language governing permissions | |
11 * and limitations under the License. | |
12 * | |
13 * When distributing Covered Code, include this CDDL HEADER in each | |
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. | |
15 * If applicable, add the following below this CDDL HEADER, with the | |
16 * fields enclosed by brackets "[]" replaced with your own identifying | |
17 * information: Portions Copyright [yyyy] [name of copyright owner] | |
18 * | |
19 * CDDL HEADER END | |
20 */ | |
21 /* | |
22 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. | |
23 * Use is subject to license terms. | |
24 */ | |
25 | |
4
1a15d5aaf794
synchronized with onnv_86 (6202) in onnv-gate
Koji Uno <koji.uno@sun.com>
parents:
0
diff
changeset
|
26 #pragma ident "%Z%%M% %I% %E% SMI" |
0 | 27 |
28 /* | |
29 * Code to maintain the runtime and on-disk filehandle mapping table for | |
30 * nfslog. | |
31 */ | |
32 | |
33 #include <assert.h> | |
34 #include <errno.h> | |
35 #include <ctype.h> | |
36 #include <nfs/nfs.h> | |
37 #include <stdlib.h> | |
38 #include <stddef.h> | |
39 #include <string.h> | |
40 #include <strings.h> | |
41 #include <syslog.h> | |
42 #include <unistd.h> | |
43 #include <dirent.h> | |
44 #include <ndbm.h> | |
45 #include <time.h> | |
46 #include <libintl.h> | |
47 #include <sys/types.h> | |
48 #include <nfs/nfs.h> | |
49 #include <nfs/nfs_log.h> | |
50 #include "fhtab.h" | |
51 #include "nfslogd.h" | |
52 | |
53 #define ROUNDUP32(val) (((val) + 3) & ~3) | |
54 | |
55 /* | |
56 * It is important that this string not match the length of the | |
57 * file handle key length NFS_FHMAXDATA | |
58 */ | |
59 #define DB_VERSION_STRING "NFSLOG_DB_VERSION" | |
60 #define DB_VERSION "1" | |
61 | |
62 #define MAX_PRUNE_REC_CNT 100000 | |
63 | |
64 fhandle_t public_fh = { 0 }; | |
65 | |
66 struct db_list { | |
67 fsid_t fsid; /* filesystem fsid */ | |
68 char *path; /* dbm filepair path */ | |
69 DBM *db; /* open dbm database */ | |
70 bool_t getall; /* TRUE if all dbm for prefix open */ | |
71 struct db_list *next; /* next db */ | |
72 }; | |
73 | |
74 static struct db_list *db_fs_list = NULL; | |
75 static char err_str[] = "DB I/O error has occurred"; | |
76 struct link_keys { | |
77 fh_secondary_key lnkey; | |
78 int lnsize; | |
79 struct link_keys *next; | |
80 }; | |
81 extern int debug; | |
82 extern time_t mapping_update_interval; | |
83 extern time_t prune_timeout; | |
84 | |
85 static int fill_link_key(char *linkkey, fhandle_t *dfh, char *name); | |
86 static struct db_list *db_get_db(char *fhpath, fsid_t *fsid, int *errorp, | |
87 int create_flag); | |
88 static struct db_list *db_get_all_databases(char *fhpath, bool_t getall); | |
89 static void debug_print_fhlist(FILE *fp, fhlist_ent *fhrecp); | |
90 static void debug_print_linkinfo(FILE *fp, linkinfo_ent *fhrecp); | |
91 static void debug_print_key(FILE *fp, char *str1, char *str2, char *key, | |
92 int ksize); | |
93 static void debug_print_key_and_data(FILE *fp, char *str1, char *str2, | |
94 char *key, int ksize, char *data, int dsize); | |
95 static int store_record(struct db_list *dbp, void *keyaddr, int keysize, | |
96 void *dataaddr, int datasize, char *str); | |
97 static void *fetch_record(struct db_list *dbp, void *keyaddr, int keysize, | |
98 void *dataaddr, int *errorp, char *str); | |
99 static int delete_record(struct db_list *dbp, void *keyaddr, int keysize, | |
100 char *str); | |
101 static int db_update_fhrec(struct db_list *dbp, void *keyaddr, int keysize, | |
102 fhlist_ent *fhrecp, char *str); | |
103 static int db_update_linkinfo(struct db_list *dbp, void *keyaddr, int keysize, | |
104 linkinfo_ent *linkp, char *str); | |
105 static fhlist_ent *create_primary_struct(struct db_list *dbp, fhandle_t *dfh, | |
106 char *name, fhandle_t *fh, uint_t flags, fhlist_ent *fhrecp, | |
107 int *errorp); | |
108 static fhlist_ent *db_add_primary(struct db_list *dbp, fhandle_t *dfh, | |
109 char *name, fhandle_t *fh, uint_t flags, fhlist_ent *fhrecp, | |
110 int *errorp); | |
111 static linkinfo_ent *get_next_link(struct db_list *dbp, char *linkkey, | |
112 int *linksizep, linkinfo_ent *linkp, void **cookiep, | |
113 int *errorp, char *msg); | |
114 static void free_link_cookies(void *cookie); | |
115 static void add_mc_path(struct db_list *dbp, fhandle_t *dfh, char *name, | |
116 fhlist_ent *fhrecp, linkinfo_ent *linkp, int *errorp); | |
117 static linkinfo_ent *create_link_struct(struct db_list *dbp, fhandle_t *dfh, | |
118 char *name, fhlist_ent *fhrecp, int *errorp); | |
119 static int db_add_secondary(struct db_list *dbp, fhandle_t *dfh, char *name, | |
120 fhandle_t *fh, fhlist_ent *fhrecp); | |
121 static linkinfo_ent *update_next_link(struct db_list *dbp, char *nextkey, | |
122 int nextsize, char *prevkey, int prevsize, int *errorp); | |
123 static int update_prev_link(struct db_list *dbp, char *nextkey, int nextsize, | |
124 char *prevkey, int prevsize); | |
125 static linkinfo_ent *update_linked_list(struct db_list *dbp, char *nextkey, | |
126 int nextsize, char *prevkey, int prevsize, int *errorp); | |
127 static int db_update_primary_new_head(struct db_list *dbp, | |
128 linkinfo_ent *dellinkp, linkinfo_ent *nextlinkp, fhlist_ent *fhrecp); | |
129 static int delete_link_by_key(struct db_list *dbp, char *linkkey, | |
130 int *linksizep, int *errorp, char *errstr); | |
131 static int delete_link(struct db_list *dbp, fhandle_t *dfh, char *name, | |
132 char *nextlinkkey, int *nextlinksizep, int *errorp, char *errstr); | |
133 | |
134 /* | |
135 * The following functions do the actual database I/O. Currently use DBM. | |
136 */ | |
137 | |
138 /* | |
139 * The "db_*" functions are functions that access the database using | |
140 * database-specific calls. Currently the only database supported is | |
141 * dbm. Because of the limitations of this database, in particular when | |
142 * it comes to manipulating records with the same key, or using multiple keys, | |
143 * the following design decisions have been made: | |
144 * | |
145 * Each file system has a separate dbm file, which are kept open as | |
146 * accessed, listed in a linked list. | |
147 * Two possible access mode are available for each file - either by | |
148 * file handle, or by directory file handle and name. Since | |
149 * dbm does not allow multiple keys, we will have a primary | |
150 * and secondary key for each file/link. | |
151 * The primary key is the pair (inode,gen) which can be obtained | |
152 * from the file handle. This points to a record with | |
153 * the full file handle and the secondary key (dfh-key,name) | |
154 * for one of the links. | |
155 * The secondary key is the pair (dfh-key,name) where dfh-key is | |
156 * the primary key for the directory and the name is the | |
157 * link name. It points to a record that contains the primary | |
158 * key for the file and to the previous and next hard link | |
159 * found for this file (if they exist). | |
160 * | |
161 * Summary of operations: | |
162 * Adding a new file: Create the primary record and secondary (link) | |
163 * record and add both to the database. The link record | |
164 * would have prev and next links set to NULL. | |
165 * | |
166 * Adding a link to a file in the database: Add the link record, | |
167 * to the head of the links list (i.e. prev = NULL, next = | |
168 * secondary key recorded in the primary record). Update | |
169 * the primary record to point to the new link, and the | |
170 * secondary record for the old head of list to point to new. | |
171 * | |
172 * Deleting a file: Delete the link record. If it is the last link | |
173 * then mark the primary record as deleted but don't delete | |
174 * that one from the database (in case some clients still | |
175 * hold the file handle). If there are other links, and the | |
176 * deleted link is the head of the list (in the primary | |
177 * record), update the primary record with the new head. | |
178 * | |
179 * Renaming a file: Add the new link and then delete the old one. | |
180 * | |
181 * Lookup by file handle (read, write, lookup, etc.) - fetch primary rec. | |
182 * Lookup by dir info (delete, link, rename) - fetch secondary rec. | |
183 * | |
184 * XXX NOTE: The code is written single-threaded. To make it multi- | |
185 * threaded, the following considerations must be made: | |
186 * 1. Changes/access to the db list must be atomic. | |
187 * 2. Changes/access for a specific file handle must be atomic | |
188 * (example: deleting a link may affect up to 4 separate database | |
189 * entries: the deleted link, the prev and next links if exist, | |
190 * and the filehandle entry, if it points to the deleted link - | |
191 * these changes must be atomic). | |
192 */ | |
193 | |
194 /* | |
195 * Create a link key given directory fh and name | |
196 */ | |
197 static int | |
198 fill_link_key(char *linkkey, fhandle_t *dfh, char *name) | |
199 { | |
200 int linksize, linksize32; | |
201 | |
202 (void) memcpy(linkkey, &dfh->fh_data, dfh->fh_len); | |
203 (void) strcpy(&linkkey[dfh->fh_len], name); | |
204 linksize = dfh->fh_len + strlen(name) + 1; | |
205 linksize32 = ROUNDUP32(linksize); | |
206 if (linksize32 > linksize) | |
207 bzero(&linkkey[linksize], linksize32 - linksize); | |
208 return (linksize32); | |
209 } | |
210 | |
211 /* | |
212 * db_get_db - gets the database for the filesystem, or creates one | |
213 * if none exists. Return the pointer for the database in *dbpp if success. | |
214 * Return 0 for success, error code otherwise. | |
215 */ | |
216 static struct db_list * | |
217 db_get_db(char *fhpath, fsid_t *fsid, int *errorp, int create_flag) | |
218 { | |
219 struct db_list *p, *newp; | |
220 char fsidstr[30]; | |
221 datum key, data; | |
222 | |
223 *errorp = 0; | |
224 for (p = db_fs_list; | |
225 (p != NULL) && memcmp(&p->fsid, fsid, sizeof (*fsid)); | |
226 p = p->next); | |
227 if (p != NULL) { | |
228 /* Found it */ | |
229 return (p); | |
230 } | |
231 /* Create it */ | |
232 if ((newp = calloc(1, sizeof (*newp))) == NULL) { | |
233 *errorp = errno; | |
234 syslog(LOG_ERR, gettext( | |
235 "db_get_db: malloc db failed: Error %s"), | |
236 strerror(*errorp)); | |
237 return (NULL); | |
238 } | |
239 (void) sprintf(fsidstr, "%08x%08x", fsid->val[0], fsid->val[1]); | |
240 if ((newp->path = malloc(strlen(fhpath) + 2 + strlen(fsidstr))) | |
241 == NULL) { | |
242 *errorp = errno; | |
243 syslog(LOG_ERR, gettext( | |
244 "db_get_db: malloc dbpath failed: Error %s"), | |
245 strerror(*errorp)); | |
246 goto err_exit; | |
247 } | |
248 (void) sprintf(newp->path, "%s.%s", fhpath, fsidstr); | |
249 /* | |
250 * The open mode is masked by UMASK. | |
251 */ | |
252 if ((newp->db = dbm_open(newp->path, create_flag | O_RDWR, 0666)) | |
253 == NULL) { | |
254 *errorp = errno; | |
255 syslog(LOG_ERR, gettext( | |
256 "db_get_db: dbm_open db '%s' failed: Error %s"), | |
257 newp->path, strerror(*errorp)); | |
258 if (*errorp == 0) /* should not happen but may */ | |
259 *errorp = -1; | |
260 goto err_exit; | |
261 } | |
262 /* | |
263 * Add the version identifier (have to check first in the | |
264 * case the db exists) | |
265 */ | |
266 key.dptr = DB_VERSION_STRING; | |
267 key.dsize = strlen(DB_VERSION_STRING); | |
268 data = dbm_fetch(newp->db, key); | |
269 if (data.dptr == NULL) { | |
270 data.dptr = DB_VERSION; | |
271 data.dsize = strlen(DB_VERSION); | |
272 (void) dbm_store(newp->db, key, data, DBM_INSERT); | |
273 } | |
274 | |
275 (void) memcpy(&newp->fsid, fsid, sizeof (*fsid)); | |
276 newp->next = db_fs_list; | |
277 db_fs_list = newp; | |
278 if (debug > 1) { | |
279 (void) printf("db_get_db: db %s opened\n", newp->path); | |
280 } | |
281 return (newp); | |
282 | |
283 err_exit: | |
284 if (newp != NULL) { | |
285 if (newp->db != NULL) { | |
286 dbm_close(newp->db); | |
287 } | |
288 if (newp->path != NULL) { | |
289 free(newp->path); | |
290 } | |
291 free(newp); | |
292 } | |
293 return (NULL); | |
294 } | |
295 | |
296 /* | |
297 * db_get_all_databases - gets the database for any filesystem. This is used | |
298 * when any database will do - typically to retrieve the path for the | |
299 * public filesystem. If any database is open - return the first one, | |
300 * otherwise, search for it using fhpath. If getall is TRUE, open all | |
301 * matching databases, and mark them (to indicate that all such were opened). | |
302 * Return the pointer for a matching database if success. | |
303 */ | |
304 static struct db_list * | |
305 db_get_all_databases(char *fhpath, bool_t getall) | |
306 { | |
307 char *dirptr, *fhdir, *fhpathname; | |
308 int len, error; | |
309 DIR *dirp; | |
310 struct dirent *dp; | |
311 fsid_t fsid; | |
312 struct db_list *dbp, *ret_dbp; | |
313 | |
314 for (dbp = db_fs_list; dbp != NULL; dbp = dbp->next) { | |
315 if (strncmp(fhpath, dbp->path, strlen(fhpath)) == 0) | |
316 break; | |
317 } | |
318 if (dbp != NULL) { | |
319 /* | |
320 * if one database for that prefix is open, and either only | |
321 * one is needed, or already opened all such databases, | |
322 * return here without exhaustive search | |
323 */ | |
324 if (!getall || dbp->getall) | |
325 return (dbp); | |
326 } | |
327 if ((fhdir = strdup(fhpath)) == NULL) { | |
328 syslog(LOG_ERR, gettext( | |
329 "db_get_all_databases: strdup '%s' Error '%s*'"), | |
330 fhpath, strerror(errno)); | |
331 return (NULL); | |
332 } | |
333 fhpathname = NULL; | |
334 ret_dbp = NULL; | |
335 if ((dirptr = strrchr(fhdir, '/')) == NULL) { | |
336 /* no directory */ | |
337 goto exit; | |
338 } | |
339 if ((fhpathname = strdup(&dirptr[1])) == NULL) { | |
340 syslog(LOG_ERR, gettext( | |
341 "db_get_all_databases: strdup '%s' Error '%s*'"), | |
342 &dirptr[1], strerror(errno)); | |
343 goto exit; | |
344 } | |
345 /* Terminate fhdir string at last '/' */ | |
346 dirptr[1] = '\0'; | |
347 /* Search the directory */ | |
348 if (debug > 2) { | |
349 (void) printf("db_get_all_databases: search '%s' for '%s*'\n", | |
350 fhdir, fhpathname); | |
351 } | |
352 if ((dirp = opendir(fhdir)) == NULL) { | |
353 syslog(LOG_ERR, gettext( | |
354 "db_get_all_databases: opendir '%s' Error '%s*'"), | |
355 fhdir, strerror(errno)); | |
356 goto exit; | |
357 } | |
358 len = strlen(fhpathname); | |
359 while ((dp = readdir(dirp)) != NULL) { | |
360 if (strncmp(fhpathname, dp->d_name, len) == 0) { | |
361 dirptr = &dp->d_name[len + 1]; | |
362 if (*(dirptr - 1) != '.') { | |
363 continue; | |
364 } | |
365 (void) sscanf(dirptr, "%08lx%08lx", | |
366 (ulong_t *)&fsid.val[0], (ulong_t *)&fsid.val[1]); | |
367 dbp = db_get_db(fhpath, &fsid, &error, 0); | |
368 if (dbp != NULL) { | |
369 ret_dbp = dbp; | |
370 if (!getall) | |
371 break; | |
372 dbp->getall = TRUE; | |
373 } | |
374 } | |
375 } | |
376 (void) closedir(dirp); | |
377 exit: | |
378 if (fhpathname != NULL) | |
379 free(fhpathname); | |
380 if (fhdir != NULL) | |
381 free(fhdir); | |
382 return (ret_dbp); | |
383 } | |
384 | |
385 static void | |
386 debug_print_key(FILE *fp, char *str1, char *str2, char *key, int ksize) | |
387 { | |
388 (void) fprintf(fp, "%s: %s key (%d) ", str1, str2, ksize); | |
389 debug_opaque_print(fp, key, ksize); | |
390 /* may be inode,name - try to print the fields */ | |
391 if (ksize >= NFS_FHMAXDATA) { | |
392 (void) fprintf(fp, ": inode "); | |
393 debug_opaque_print(fp, &key[2], sizeof (int)); | |
394 (void) fprintf(fp, ", gen "); | |
395 debug_opaque_print(fp, &key[2 + sizeof (int)], sizeof (int)); | |
396 if (ksize > NFS_FHMAXDATA) { | |
397 (void) fprintf(fp, ", name '%s'", &key[NFS_FHMAXDATA]); | |
398 } | |
399 } | |
400 (void) fprintf(fp, "\n"); | |
401 } | |
402 | |
403 static void | |
404 debug_print_linkinfo(FILE *fp, linkinfo_ent *linkp) | |
405 { | |
406 if (linkp == NULL) | |
407 return; | |
408 (void) fprintf(fp, "linkinfo:\ndfh: "); | |
409 debug_opaque_print(fp, (void *)&linkp->dfh, sizeof (linkp->dfh)); | |
410 (void) fprintf(fp, "\nname: '%s'", LN_NAME(linkp)); | |
411 (void) fprintf(fp, "\nmtime 0x%x, atime 0x%x, flags 0x%x, reclen %d\n", | |
412 linkp->mtime, linkp->atime, linkp->flags, linkp->reclen); | |
413 (void) fprintf(fp, "offsets: fhkey %d, name %d, next %d, prev %d\n", | |
414 linkp->fhkey_offset, linkp->name_offset, linkp->next_offset, | |
415 linkp->prev_offset); | |
416 debug_print_key(fp, "fhkey", "", LN_FHKEY(linkp), LN_FHKEY_LEN(linkp)); | |
417 debug_print_key(fp, "next", "", LN_NEXT(linkp), LN_NEXT_LEN(linkp)); | |
418 debug_print_key(fp, "prev", "", LN_PREV(linkp), LN_PREV_LEN(linkp)); | |
419 } | |
420 | |
421 static void | |
422 debug_print_fhlist(FILE *fp, fhlist_ent *fhrecp) | |
423 { | |
424 if (fhrecp == NULL) | |
425 return; | |
426 (void) fprintf(fp, "fhrec:\nfh: "); | |
427 debug_opaque_print(fp, (void *)&fhrecp->fh, sizeof (fhrecp->fh)); | |
428 (void) fprintf(fp, "name '%s', dfh: ", fhrecp->name); | |
429 debug_opaque_print(fp, (void *)&fhrecp->dfh, sizeof (fhrecp->dfh)); | |
430 (void) fprintf(fp, "\nmtime 0x%x, atime 0x%x, flags 0x%x, reclen %d\n", | |
431 fhrecp->mtime, fhrecp->atime, fhrecp->flags, fhrecp->reclen); | |
432 } | |
433 | |
434 static void | |
435 debug_print_key_and_data(FILE *fp, char *str1, char *str2, char *key, | |
436 int ksize, char *data, int dsize) | |
437 { | |
438 debug_print_key(fp, str1, str2, key, ksize); | |
439 (void) fprintf(fp, " ==> (%p,%d)\n", (void *)data, dsize); | |
440 if (ksize > NFS_FHMAXDATA) { | |
441 linkinfo_ent inf; | |
442 /* probably a link struct */ | |
443 (void) memcpy(&inf, data, sizeof (linkinfo_ent)); | |
444 debug_print_linkinfo(fp, &inf); | |
445 } else if (ksize == NFS_FHMAXDATA) { | |
446 fhlist_ent inf; | |
447 /* probably an fhlist struct */ | |
448 (void) memcpy(&inf, data, sizeof (linkinfo_ent)); | |
449 debug_print_fhlist(fp, &inf); | |
450 } else { | |
451 /* don't know... */ | |
452 debug_opaque_print(fp, data, dsize); | |
453 } | |
454 } | |
455 | |
456 /* | |
457 * store_record - store the record in the database and return 0 for success | |
458 * or error code otherwise. | |
459 */ | |
460 static int | |
461 store_record(struct db_list *dbp, void *keyaddr, int keysize, void *dataaddr, | |
462 int datasize, char *str) | |
463 { | |
464 datum key, data; | |
465 int error; | |
466 char *errfmt = "store_record: dbm_store failed, Error: %s\n"; | |
467 char *err; | |
468 | |
469 errno = 0; | |
470 key.dptr = keyaddr; | |
471 key.dsize = keysize; | |
472 data.dptr = dataaddr; | |
473 data.dsize = datasize; | |
474 | |
475 if (debug > 2) { | |
476 debug_print_key_and_data(stdout, str, "dbm_store:\n ", | |
477 key.dptr, key.dsize, data.dptr, data.dsize); | |
478 } | |
479 if (dbm_store(dbp->db, key, data, DBM_REPLACE) < 0) { | |
480 /* Could not store */ | |
481 error = dbm_error(dbp->db); | |
482 dbm_clearerr(dbp->db); | |
483 | |
484 if (error) { | |
485 if (errno) | |
486 err = strerror(errno); | |
487 else { | |
488 err = err_str; | |
489 errno = EIO; | |
490 } | |
491 } else { /* should not happen but sometimes does */ | |
492 err = err_str; | |
493 errno = -1; | |
494 } | |
495 if (debug) { | |
496 debug_print_key(stderr, str, "store_record:" | |
497 "dbm_store:\n", key.dptr, key.dsize); | |
498 (void) fprintf(stderr, errfmt, err); | |
499 } else | |
500 syslog(LOG_ERR, gettext(errfmt), err); | |
501 return (errno); | |
502 } | |
503 return (0); | |
504 } | |
505 | |
506 /* | |
507 * fetch_record - fetch the record from the database and return 0 for success | |
508 * and errno for failure. | |
509 * dataaddr is an optional valid address for the result. If dataaddr | |
510 * is non-null, then that memory is already alloc'd. Else, alloc it, and | |
511 * the caller must free the returned struct when done. | |
512 */ | |
513 static void * | |
514 fetch_record(struct db_list *dbp, void *keyaddr, int keysize, void *dataaddr, | |
515 int *errorp, char *str) | |
516 { | |
517 datum key, data; | |
518 char *errfmt = "fetch_record: dbm_fetch failed, Error: %s\n"; | |
519 char *err; | |
520 | |
521 errno = 0; | |
522 *errorp = 0; | |
523 key.dptr = keyaddr; | |
524 key.dsize = keysize; | |
525 | |
526 data = dbm_fetch(dbp->db, key); | |
527 if (data.dptr == NULL) { | |
528 /* see if there is a database error */ | |
529 if (dbm_error(dbp->db)) { | |
530 /* clear and report the database error */ | |
531 dbm_clearerr(dbp->db); | |
532 *errorp = EIO; | |
533 err = strerror(*errorp); | |
534 syslog(LOG_ERR, gettext(errfmt), err); | |
535 } else { | |
536 /* primary record not in database */ | |
537 *errorp = ENOENT; | |
538 } | |
539 if (debug > 3) { | |
540 err = strerror(*errorp); | |
541 debug_print_key(stderr, str, "fetch_record:" | |
542 "dbm_fetch:\n", key.dptr, key.dsize); | |
543 (void) fprintf(stderr, errfmt, err); | |
544 } | |
545 return (NULL); | |
546 } | |
547 | |
548 /* copy to local struct because dbm may return non-aligned pointers */ | |
549 if ((dataaddr == NULL) && | |
550 ((dataaddr = malloc(data.dsize)) == NULL)) { | |
551 *errorp = errno; | |
552 syslog(LOG_ERR, gettext( | |
553 "%s: dbm_fetch - malloc %ld: Error %s"), | |
554 str, data.dsize, strerror(*errorp)); | |
555 return (NULL); | |
556 } | |
557 (void) memcpy(dataaddr, data.dptr, data.dsize); | |
558 if (debug > 3) { | |
559 debug_print_key_and_data(stdout, str, "fetch_record:" | |
560 "dbm_fetch:\n", key.dptr, key.dsize, | |
561 dataaddr, data.dsize); | |
562 } | |
563 *errorp = 0; | |
564 return (dataaddr); | |
565 } | |
566 | |
567 /* | |
568 * delete_record - delete the record from the database and return 0 for success | |
569 * or error code for failure. | |
570 */ | |
571 static int | |
572 delete_record(struct db_list *dbp, void *keyaddr, int keysize, char *str) | |
573 { | |
574 datum key; | |
575 int error = 0; | |
576 char *errfmt = "delete_record: dbm_delete failed, Error: %s\n"; | |
577 char *err; | |
578 | |
579 errno = 0; | |
580 key.dptr = keyaddr; | |
581 key.dsize = keysize; | |
582 | |
583 if (debug > 2) { | |
584 debug_print_key(stdout, str, "delete_record:" | |
585 "dbm_delete:\n", key.dptr, key.dsize); | |
586 } | |
587 if (dbm_delete(dbp->db, key) < 0) { | |
588 error = dbm_error(dbp->db); | |
589 dbm_clearerr(dbp->db); | |
590 | |
591 if (error) { | |
592 if (errno) | |
593 err = strerror(errno); | |
594 else { | |
595 err = err_str; | |
596 errno = EIO; | |
597 } | |
598 } else { /* should not happen but sometimes does */ | |
599 err = err_str; | |
600 errno = -1; | |
601 } | |
602 if (debug) { | |
603 debug_print_key(stderr, str, "delete_record:" | |
604 "dbm_delete:\n", key.dptr, key.dsize); | |
605 (void) fprintf(stderr, errfmt, err); | |
606 } else | |
607 syslog(LOG_ERR, gettext(errfmt), err); | |
608 } | |
609 return (errno); | |
610 } | |
611 | |
612 /* | |
613 * db_update_fhrec - puts fhrec in db with updated atime if more than | |
614 * mapping_update_interval seconds passed. Return 0 if success, error otherwise. | |
615 */ | |
616 static int | |
617 db_update_fhrec(struct db_list *dbp, void *keyaddr, int keysize, | |
618 fhlist_ent *fhrecp, char *str) | |
619 { | |
620 time_t cur_time = time(0); | |
621 | |
622 if (difftime(cur_time, fhrecp->atime) >= mapping_update_interval) { | |
623 fhrecp->atime = cur_time; | |
624 return (store_record(dbp, keyaddr, keysize, | |
625 fhrecp, fhrecp->reclen, str)); | |
626 } | |
627 return (0); | |
628 } | |
629 | |
630 /* | |
631 * db_update_linkinfo - puts linkinfo in db with updated atime if more than | |
632 * mapping_update_interval seconds passed. Return 0 if success, error otherwise. | |
633 */ | |
634 static int | |
635 db_update_linkinfo(struct db_list *dbp, void *keyaddr, int keysize, | |
636 linkinfo_ent *linkp, char *str) | |
637 { | |
638 time_t cur_time = time(0); | |
639 | |
640 if (difftime(cur_time, linkp->atime) >= mapping_update_interval) { | |
641 linkp->atime = cur_time; | |
642 return (store_record(dbp, keyaddr, keysize, | |
643 linkp, linkp->reclen, str)); | |
644 } | |
645 return (0); | |
646 } | |
647 | |
648 /* | |
649 * create_primary_struct - add primary record to the database. | |
650 * Database must be open when this function is called. | |
651 * If success, return the added database entry. fhrecp may be used to | |
652 * provide an existing memory area, else malloc it. If failed, *errorp | |
653 * contains the error code and return NULL. | |
654 */ | |
655 static fhlist_ent * | |
656 create_primary_struct(struct db_list *dbp, fhandle_t *dfh, char *name, | |
657 fhandle_t *fh, uint_t flags, fhlist_ent *fhrecp, int *errorp) | |
658 { | |
659 int reclen, reclen1; | |
660 fhlist_ent *new_fhrecp = fhrecp; | |
661 | |
662 reclen1 = offsetof(fhlist_ent, name) + strlen(name) + 1; | |
663 reclen = ROUNDUP32(reclen1); | |
664 if (fhrecp == NULL) { /* allocated the memory */ | |
665 if ((new_fhrecp = malloc(reclen)) == NULL) { | |
666 *errorp = errno; | |
667 syslog(LOG_ERR, gettext( | |
668 "create_primary_struct: malloc %d Error %s"), | |
669 reclen, strerror(*errorp)); | |
670 return (NULL); | |
671 } | |
672 } | |
673 /* Fill in the fields */ | |
674 (void) memcpy(&new_fhrecp->fh, fh, sizeof (*fh)); | |
675 (void) memcpy(&new_fhrecp->dfh, dfh, sizeof (*dfh)); | |
676 new_fhrecp->flags = flags; | |
677 if (dfh == &public_fh) | |
678 new_fhrecp->flags |= PUBLIC_PATH; | |
679 else | |
680 new_fhrecp->flags &= ~PUBLIC_PATH; | |
681 new_fhrecp->mtime = time(0); | |
682 new_fhrecp->atime = new_fhrecp->mtime; | |
683 (void) strcpy(new_fhrecp->name, name); | |
684 if (reclen1 < reclen) { | |
685 bzero((char *)((uintptr_t)new_fhrecp + reclen1), | |
686 reclen - reclen1); | |
687 } | |
688 new_fhrecp->reclen = reclen; | |
689 *errorp = store_record(dbp, &fh->fh_data, fh->fh_len, new_fhrecp, | |
690 new_fhrecp->reclen, "create_primary_struct"); | |
691 if (*errorp != 0) { | |
692 /* Could not store */ | |
693 if (fhrecp == NULL) /* caller did not supply pointer */ | |
694 free(new_fhrecp); | |
695 return (NULL); | |
696 } | |
697 return (new_fhrecp); | |
698 } | |
699 | |
700 /* | |
701 * db_add_primary - add primary record to the database. | |
702 * If record already in and live, return it (even if for a different link). | |
703 * If in database but marked deleted, replace it. If not in database, add it. | |
704 * Database must be open when this function is called. | |
705 * If success, return the added database entry. fhrecp may be used to | |
706 * provide an existing memory area, else malloc it. If failed, *errorp | |
707 * contains the error code and return NULL. | |
708 */ | |
709 static fhlist_ent * | |
710 db_add_primary(struct db_list *dbp, fhandle_t *dfh, char *name, fhandle_t *fh, | |
711 uint_t flags, fhlist_ent *fhrecp, int *errorp) | |
712 { | |
713 fhlist_ent *new_fhrecp; | |
714 fh_primary_key fhkey; | |
715 | |
716 if (debug > 2) | |
717 (void) printf("db_add_primary entered: name '%s'\n", name); | |
718 | |
719 bcopy(&fh->fh_data, fhkey, fh->fh_len); | |
720 new_fhrecp = fetch_record(dbp, fhkey, fh->fh_len, (void *)fhrecp, | |
721 errorp, "db_add_primary"); | |
722 if (new_fhrecp != NULL) { | |
723 /* primary record is in the database */ | |
724 /* Update atime if needed */ | |
725 *errorp = db_update_fhrec(dbp, fhkey, fh->fh_len, new_fhrecp, | |
726 "db_add_primary put fhrec"); | |
727 if (debug > 2) | |
728 (void) printf("db_add_primary exits(2): name '%s'\n", | |
729 name); | |
730 return (new_fhrecp); | |
731 } | |
732 /* primary record not in database - create it */ | |
733 new_fhrecp = create_primary_struct(dbp, dfh, name, fh, flags, | |
734 fhrecp, errorp); | |
735 if (new_fhrecp == NULL) { | |
736 /* Could not store */ | |
737 if (debug > 2) | |
738 (void) printf( | |
739 "db_add_primary exits(1): name '%s' Error %s\n", | |
740 name, ((*errorp >= 0) ? strerror(*errorp) : | |
741 "Unknown")); | |
742 | |
743 return (NULL); | |
744 } | |
745 if (debug > 2) | |
746 (void) printf("db_add_primary exits(0): name '%s'\n", name); | |
747 return (new_fhrecp); | |
748 } | |
749 | |
750 /* | |
751 * get_next_link - get and check the next link in the chain. | |
752 * Re-use space if linkp param non-null. Also set *linkkey and *linksizep | |
753 * to values for next link (*linksizep set to 0 if last link). | |
754 * cookie is used to detect corrupted link entries XXXXXXX | |
755 * Return the link pointer or NULL if none. | |
756 */ | |
757 static linkinfo_ent * | |
758 get_next_link(struct db_list *dbp, char *linkkey, int *linksizep, | |
759 linkinfo_ent *linkp, void **cookiep, int *errorp, char *msg) | |
760 { | |
761 int linksize, nextsize; | |
762 char *nextkey; | |
763 linkinfo_ent *new_linkp = linkp; | |
764 struct link_keys *lnp; | |
765 | |
766 linksize = *linksizep; | |
767 if (linksize == 0) | |
768 return (NULL); | |
769 *linksizep = 0; | |
770 new_linkp = fetch_record(dbp, linkkey, linksize, (void *)linkp, | |
771 errorp, msg); | |
772 if (new_linkp == NULL) | |
773 return (NULL); | |
774 | |
775 /* Set linkkey to point to next record */ | |
776 nextsize = LN_NEXT_LEN(new_linkp); | |
777 if (nextsize == 0) | |
778 return (new_linkp); | |
779 | |
780 /* Add this key to the cookie list */ | |
781 if ((lnp = malloc(sizeof (struct link_keys))) == NULL) { | |
782 syslog(LOG_ERR, gettext("get_next_key: malloc error %s\n"), | |
783 strerror(errno)); | |
784 if ((new_linkp != NULL) && (linkp == NULL)) | |
785 free(new_linkp); | |
786 return (NULL); | |
787 } | |
788 (void) memcpy(lnp->lnkey, linkkey, linksize); | |
789 lnp->lnsize = linksize; | |
790 lnp->next = *(struct link_keys **)cookiep; | |
791 *cookiep = (void *)lnp; | |
792 | |
793 /* Make sure record does not point to itself or other internal loops */ | |
794 nextkey = LN_NEXT(new_linkp); | |
795 for (; lnp != NULL; lnp = lnp->next) { | |
796 if ((nextsize == lnp->lnsize) && (memcmp( | |
797 lnp->lnkey, nextkey, nextsize) == 0)) { | |
798 | |
799 /* | |
800 * XXX This entry's next pointer points to | |
801 * itself. This is only a work-around, remove | |
802 * this check once bug 4203186 is fixed. | |
803 */ | |
804 if (debug) { | |
805 (void) fprintf(stderr, | |
806 "%s: get_next_link: last record invalid.\n", | |
807 msg); | |
808 debug_print_key_and_data(stderr, msg, | |
809 "invalid rec:\n ", linkkey, linksize, | |
810 (char *)new_linkp, new_linkp->reclen); | |
811 } | |
812 /* Return as if this is the last link */ | |
813 return (new_linkp); | |
814 } | |
815 } | |
816 (void) memcpy(linkkey, nextkey, nextsize); | |
817 *linksizep = nextsize; | |
818 return (new_linkp); | |
819 } | |
820 | |
821 /* | |
822 * free_link_cookies - free the cookie list | |
823 */ | |
824 static void | |
825 free_link_cookies(void *cookie) | |
826 { | |
827 struct link_keys *dellnp, *lnp; | |
828 | |
829 lnp = (struct link_keys *)cookie; | |
830 while (lnp != NULL) { | |
831 dellnp = lnp; | |
832 lnp = lnp->next; | |
833 free(dellnp); | |
834 } | |
835 } | |
836 | |
837 /* | |
838 * add_mc_path - add a mc link to a file that has other links. Add it at end | |
839 * of linked list. Called when it's known there are other links. | |
840 */ | |
841 static void | |
842 add_mc_path(struct db_list *dbp, fhandle_t *dfh, char *name, | |
843 fhlist_ent *fhrecp, linkinfo_ent *linkp, int *errorp) | |
844 { | |
845 fh_secondary_key linkkey; | |
846 int linksize, len; | |
847 linkinfo_ent lastlink, *lastlinkp; | |
848 void *cookie; | |
849 | |
850 linksize = fill_link_key(linkkey, &fhrecp->dfh, fhrecp->name); | |
851 cookie = NULL; | |
852 do { | |
853 lastlinkp = get_next_link(dbp, linkkey, &linksize, &lastlink, | |
854 &cookie, errorp, "add_mc_path"); | |
855 } while (linksize > 0); | |
856 free_link_cookies(cookie); | |
857 /* reached end of list */ | |
858 if (lastlinkp == NULL) { | |
859 /* nothing to do */ | |
860 if (debug > 1) { | |
861 (void) fprintf(stderr, "add_mc_path link is null\n"); | |
862 } | |
863 return; | |
864 } | |
865 /* Add new link after last link */ | |
866 /* | |
867 * next - link key for the next in the list - add at end so null. | |
868 * prev - link key for the previous link in the list. | |
869 */ | |
870 linkp->prev_offset = linkp->next_offset; /* aligned */ | |
871 linksize = fill_link_key(LN_PREV(linkp), &lastlinkp->dfh, | |
872 LN_NAME(lastlinkp)); | |
873 linkp->reclen = linkp->prev_offset + linksize; /* aligned */ | |
874 | |
875 /* Add the link information to the database */ | |
876 linksize = fill_link_key(linkkey, dfh, name); | |
877 *errorp = store_record(dbp, linkkey, linksize, | |
878 linkp, linkp->reclen, "add_mc_path"); | |
879 if (*errorp != 0) | |
880 return; | |
881 | |
882 /* Now update previous last link to point forward to new link */ | |
883 /* Copy prev link out since it's going to be overwritten */ | |
884 linksize = LN_PREV_LEN(lastlinkp); | |
885 (void) memcpy(linkkey, LN_PREV(lastlinkp), linksize); | |
886 /* Update previous last link to point to new one */ | |
887 len = fill_link_key(LN_NEXT(lastlinkp), dfh, name); | |
888 lastlinkp->prev_offset = lastlinkp->next_offset + len; /* aligned */ | |
889 (void) memcpy(LN_PREV(lastlinkp), linkkey, linksize); | |
890 lastlinkp->reclen = lastlinkp->prev_offset + linksize; | |
891 /* Update the link information to the database */ | |
892 linksize = fill_link_key(linkkey, &lastlinkp->dfh, LN_NAME(lastlinkp)); | |
893 *errorp = store_record(dbp, linkkey, linksize, | |
894 lastlinkp, lastlinkp->reclen, "add_mc_path prev"); | |
895 } | |
896 | |
897 /* | |
898 * create_link_struct - create the secondary struct. | |
899 * (dfh,name) is the secondary key, fhrec is the primary record for the file | |
900 * and linkpp is a place holder for the record (could be null). | |
901 * Insert the record to the database. | |
902 * Return 0 if success, error otherwise. | |
903 */ | |
904 static linkinfo_ent * | |
905 create_link_struct(struct db_list *dbp, fhandle_t *dfh, char *name, | |
906 fhlist_ent *fhrecp, int *errorp) | |
907 { | |
908 fh_secondary_key linkkey; | |
909 int len, linksize; | |
910 linkinfo_ent *linkp; | |
911 | |
912 if ((linkp = malloc(sizeof (linkinfo_ent))) == NULL) { | |
913 *errorp = errno; | |
914 syslog(LOG_ERR, gettext( | |
915 "create_link_struct: malloc failed: Error %s"), | |
916 strerror(*errorp)); | |
917 return (NULL); | |
918 } | |
919 if (dfh == &public_fh) | |
920 linkp->flags |= PUBLIC_PATH; | |
921 else | |
922 linkp->flags &= ~PUBLIC_PATH; | |
923 (void) memcpy(&linkp->dfh, dfh, sizeof (*dfh)); | |
924 linkp->mtime = time(0); | |
925 linkp->atime = linkp->mtime; | |
926 /* Calculate offsets of variable fields */ | |
927 /* fhkey - primary key (inode/gen) */ | |
928 /* name - component name (in directory dfh) */ | |
929 linkp->fhkey_offset = ROUNDUP32(offsetof(linkinfo_ent, varbuf)); | |
930 len = fill_link_key(LN_FHKEY(linkp), &fhrecp->fh, name); | |
931 linkp->name_offset = linkp->fhkey_offset + fhrecp->fh.fh_len; | |
932 linkp->next_offset = linkp->fhkey_offset + len; /* aligned */ | |
933 /* | |
934 * next - link key for the next link in the list - NULL if it's | |
935 * the first link. If this is the public fs, only one link allowed. | |
936 * Avoid setting a multi-component path as primary path, | |
937 * unless no choice. | |
938 */ | |
939 len = 0; | |
940 if (memcmp(&fhrecp->dfh, dfh, sizeof (*dfh)) || | |
941 strcmp(fhrecp->name, name)) { | |
942 /* different link than the one that's in the record */ | |
943 if (dfh == &public_fh) { | |
944 /* parent is public fh - either multi-comp or root */ | |
945 if (memcmp(&fhrecp->fh, &public_fh, | |
946 sizeof (public_fh))) { | |
947 /* multi-comp path */ | |
948 add_mc_path(dbp, dfh, name, fhrecp, linkp, | |
949 errorp); | |
950 if (*errorp != 0) { | |
951 free(linkp); | |
952 return (NULL); | |
953 } | |
954 return (linkp); | |
955 } | |
956 } else { | |
957 /* new link to a file with a different one already */ | |
958 len = fill_link_key(LN_NEXT(linkp), &fhrecp->dfh, | |
959 fhrecp->name); | |
960 } | |
961 } | |
962 /* | |
963 * prev - link key for the previous link in the list - since we | |
964 * always insert at the front of the list, it's always initially NULL. | |
965 */ | |
966 linkp->prev_offset = linkp->next_offset + len; /* aligned */ | |
967 linkp->reclen = linkp->prev_offset; | |
968 | |
969 /* Add the link information to the database */ | |
970 linksize = fill_link_key(linkkey, dfh, name); | |
971 *errorp = store_record(dbp, linkkey, linksize, linkp, linkp->reclen, | |
972 "create_link_struct"); | |
973 if (*errorp != 0) { | |
974 free(linkp); | |
975 return (NULL); | |
976 } | |
977 return (linkp); | |
978 } | |
979 | |
980 /* | |
981 * db_add_secondary - add secondary record to the database (for the directory | |
982 * information). | |
983 * Assumes this is a new link, not yet in the database, and that the primary | |
984 * record is already in. | |
985 * If fhrecp is non-null, then fhrecp is the primary record. | |
986 * Database must be open when this function is called. | |
987 * Return 0 if success, error code otherwise. | |
988 */ | |
989 static int | |
990 db_add_secondary(struct db_list *dbp, fhandle_t *dfh, char *name, | |
991 fhandle_t *fh, fhlist_ent *fhrecp) | |
992 { | |
993 int nextsize, len, error; | |
994 linkinfo_ent nextlink, *newlinkp, *nextlinkp; | |
995 uint_t fhflags; | |
996 char *nextaddr; | |
997 fhlist_ent *new_fhrecp = fhrecp; | |
998 fh_primary_key fhkey; | |
999 | |
1000 if (debug > 2) | |
1001 (void) printf("db_add_secondary entered: name '%s'\n", name); | |
1002 | |
1003 bcopy(&fh->fh_data, fhkey, fh->fh_len); | |
1004 if (fhrecp == NULL) { | |
1005 /* Fetch the primary record */ | |
1006 new_fhrecp = fetch_record(dbp, fhkey, fh->fh_len, NULL, | |
1007 &error, "db_add_secondary primary"); | |
1008 if (new_fhrecp == NULL) { | |
1009 return (error); | |
1010 } | |
1011 } | |
1012 /* Update fhrec atime if needed */ | |
1013 error = db_update_fhrec(dbp, fhkey, fh->fh_len, new_fhrecp, | |
1014 "db_add_secondary primary"); | |
1015 fhflags = new_fhrecp->flags; | |
1016 /* now create and insert the secondary record */ | |
1017 newlinkp = create_link_struct(dbp, dfh, name, new_fhrecp, &error); | |
1018 if (fhrecp == NULL) { | |
1019 free(new_fhrecp); | |
1020 new_fhrecp = NULL; | |
1021 } | |
1022 if (newlinkp == NULL) { | |
1023 if (debug > 2) | |
1024 (void) printf("create_link_struct '%s' Error %s\n", | |
1025 name, ((error >= 0) ? strerror(error) : | |
1026 "Unknown")); | |
1027 return (error); | |
1028 } | |
1029 nextsize = LN_NEXT_LEN(newlinkp); | |
1030 if (nextsize == 0) { | |
1031 /* No next - can exit now */ | |
1032 if (debug > 2) | |
1033 (void) printf("db_add_secondary: no next link\n"); | |
1034 free(newlinkp); | |
1035 return (0); | |
1036 } | |
1037 | |
1038 /* | |
1039 * Update the linked list to point to new head: replace head of | |
1040 * list in the primary record, then update previous secondary record | |
1041 * to point to new head | |
1042 */ | |
1043 new_fhrecp = create_primary_struct(dbp, dfh, name, fh, fhflags, | |
1044 new_fhrecp, &error); | |
1045 if (new_fhrecp == NULL) { | |
1046 if (debug > 2) | |
1047 (void) printf( | |
1048 "db_add_secondary: replace primary failed\n"); | |
1049 free(newlinkp); | |
1050 return (error); | |
1051 } else if (fhrecp == NULL) { | |
1052 free(new_fhrecp); | |
1053 } | |
1054 | |
1055 /* | |
1056 * newlink is the new head of the list, with its "next" pointing to | |
1057 * the old head, and its "prev" pointing to NULL. We now need to | |
1058 * modify the "next" entry to have its "prev" point to the new entry. | |
1059 */ | |
1060 nextaddr = LN_NEXT(newlinkp); | |
1061 if (debug > 2) { | |
1062 debug_print_key(stderr, "db_add_secondary", "next key\n ", | |
1063 nextaddr, nextsize); | |
1064 } | |
1065 /* Get the next link entry from the database */ | |
1066 nextlinkp = fetch_record(dbp, nextaddr, nextsize, (void *)&nextlink, | |
1067 &error, "db_add_secondary next link"); | |
1068 if (nextlinkp == NULL) { | |
1069 if (debug > 2) | |
1070 (void) printf( | |
1071 "db_add_secondary: fetch next link failed\n"); | |
1072 free(newlinkp); | |
1073 return (error); | |
1074 } | |
1075 | |
1076 /* | |
1077 * since the "prev" field is the only field to be changed, and it's | |
1078 * the last in the link record, we only need to modify it (and reclen). | |
1079 * Re-use link to update the next record. | |
1080 */ | |
1081 len = fill_link_key(LN_PREV(nextlinkp), dfh, name); | |
1082 nextlinkp->reclen = nextlinkp->prev_offset + len; | |
1083 error = store_record(dbp, nextaddr, nextsize, nextlinkp, | |
1084 nextlinkp->reclen, "db_add_secondary"); | |
1085 if (debug > 2) | |
1086 (void) printf( | |
1087 "db_add_secondary exits(%d): name '%s'\n", error, name); | |
1088 free(newlinkp); | |
1089 return (error); | |
1090 } | |
1091 | |
1092 /* | |
1093 * Update the next link to point to the new prev. | |
1094 * Return 0 for success, error code otherwise. | |
1095 * If successful, and nextlinkpp is non-null, | |
1096 * *nextlinkpp contains the record for the next link, since | |
1097 * we may will it if the primary record should be updated. | |
1098 */ | |
1099 static linkinfo_ent * | |
1100 update_next_link(struct db_list *dbp, char *nextkey, int nextsize, | |
1101 char *prevkey, int prevsize, int *errorp) | |
1102 { | |
1103 linkinfo_ent *nextlinkp, *linkp1; | |
1104 | |
1105 if ((nextlinkp = malloc(sizeof (linkinfo_ent))) == NULL) { | |
1106 *errorp = errno; | |
1107 syslog(LOG_ERR, gettext( | |
1108 "update_next_link: malloc next Error %s"), | |
1109 strerror(*errorp)); | |
1110 return (NULL); | |
1111 } | |
1112 linkp1 = nextlinkp; | |
1113 nextlinkp = fetch_record(dbp, nextkey, nextsize, nextlinkp, | |
1114 errorp, "update next"); | |
1115 /* if there is no next record - ok */ | |
1116 if (nextlinkp == NULL) { | |
1117 /* Return no error */ | |
1118 *errorp = 0; | |
1119 free(linkp1); | |
1120 return (NULL); | |
1121 } | |
1122 /* Set its prev to the prev of the deleted record */ | |
1123 nextlinkp->reclen = ROUNDUP32(nextlinkp->reclen - | |
1124 LN_PREV_LEN(nextlinkp) + prevsize); | |
1125 /* Change the len and set prev */ | |
1126 if (prevsize > 0) { | |
1127 (void) memcpy(LN_PREV(nextlinkp), prevkey, prevsize); | |
1128 } | |
1129 /* No other changes needed because prev is last field */ | |
1130 *errorp = store_record(dbp, nextkey, nextsize, nextlinkp, | |
1131 nextlinkp->reclen, "update_next"); | |
1132 if (*errorp != 0) { | |
1133 free(nextlinkp); | |
1134 nextlinkp = NULL; | |
1135 } | |
1136 return (nextlinkp); | |
1137 } | |
1138 | |
1139 /* | |
1140 * Update the prev link to point to the new next. | |
1141 * Return 0 for success, error code otherwise. | |
1142 */ | |
1143 static int | |
1144 update_prev_link(struct db_list *dbp, char *nextkey, int nextsize, | |
1145 char *prevkey, int prevsize) | |
1146 { | |
1147 linkinfo_ent prevlink, *prevlinkp; | |
1148 int diff, error; | |
1149 | |
1150 /* Update its next to the given one */ | |
1151 prevlinkp = fetch_record(dbp, prevkey, prevsize, &prevlink, &error, | |
1152 "update prev"); | |
1153 /* if error there is no next record - ok */ | |
1154 if (prevlinkp == NULL) { | |
1155 return (0); | |
1156 } | |
1157 diff = nextsize - LN_NEXT_LEN(prevlinkp); | |
1158 prevlinkp->reclen = ROUNDUP32(prevlinkp->reclen + diff); | |
1159 /* Change the len and set next - may push prev */ | |
1160 if (diff != 0) { | |
1161 char *ptr = LN_PREV(prevlinkp); | |
1162 | |
1163 prevlinkp->prev_offset += diff; | |
1164 (void) memcpy(LN_PREV(prevlinkp), ptr, LN_PREV_LEN(prevlinkp)); | |
1165 } | |
1166 if (nextsize > 0) { | |
1167 (void) memcpy(LN_NEXT(prevlinkp), nextkey, nextsize); | |
1168 } | |
1169 /* Store updated record */ | |
1170 error = store_record(dbp, prevkey, prevsize, prevlinkp, | |
1171 prevlinkp->reclen, "update_prev"); | |
1172 return (error); | |
1173 } | |
1174 | |
1175 /* | |
1176 * update_linked_list - update the next link to point back to prev, and vice | |
1177 * versa. Normally called by delete_link to drop the deleted link from the | |
1178 * linked list of hard links for the file. next and prev are the keys of next | |
1179 * and previous links for the deleted link in the list (could be NULL). | |
1180 * Return 0 for success, error code otherwise. | |
1181 * If successful, and nextlinkpp is non-null, | |
1182 * return the record for the next link, since | |
1183 * if the primary record should be updated we'll need it. In this case, | |
1184 * actually allocate the space for it because we can't tell otherwise. | |
1185 */ | |
1186 static linkinfo_ent * | |
1187 update_linked_list(struct db_list *dbp, char *nextkey, int nextsize, | |
1188 char *prevkey, int prevsize, int *errorp) | |
1189 { | |
1190 linkinfo_ent *nextlinkp = NULL; | |
1191 | |
1192 *errorp = 0; | |
1193 if (nextsize > 0) { | |
1194 nextlinkp = update_next_link(dbp, nextkey, nextsize, | |
1195 prevkey, prevsize, errorp); | |
1196 if (nextlinkp == NULL) { | |
1197 /* not an error if no next link */ | |
1198 if (*errorp != 0) { | |
1199 if (debug > 1) { | |
1200 (void) fprintf(stderr, | |
1201 "update_next_link Error %s\n", | |
1202 ((*errorp >= 0) ? strerror(*errorp) : | |
1203 "Unknown")); | |
1204 } | |
1205 return (NULL); | |
1206 } | |
1207 } | |
1208 } | |
1209 if (prevsize > 0) { | |
1210 *errorp = update_prev_link(dbp, nextkey, nextsize, | |
1211 prevkey, prevsize); | |
1212 if (*errorp != 0) { | |
1213 if (debug > 1) { | |
1214 (void) fprintf(stderr, | |
1215 "update_prev_link Error %s\n", | |
1216 ((*errorp >= 0) ? strerror(*errorp) : | |
1217 "Unknown")); | |
1218 } | |
1219 if (nextlinkp != NULL) | |
1220 free(nextlinkp); | |
1221 nextlinkp = NULL; | |
1222 } | |
1223 } | |
1224 return (nextlinkp); | |
1225 } | |
1226 | |
1227 /* | |
1228 * db_update_primary_new_head - Update a primary record that the head of | |
1229 * the list is deleted. Similar to db_add_primary, but the primary record | |
1230 * must exist, and is always replaced with one pointing to the new link, | |
1231 * unless it does not point to the deleted link. If the link we deleted | |
1232 * was the last link, the delete the primary record as well. | |
1233 * Return 0 for success, error code otherwise. | |
1234 */ | |
1235 static int | |
1236 db_update_primary_new_head(struct db_list *dbp, linkinfo_ent *dellinkp, | |
1237 linkinfo_ent *nextlinkp, fhlist_ent *fhrecp) | |
1238 { | |
1239 int error; | |
1240 char *name, *next_name; | |
1241 fhandle_t *dfh; | |
1242 fh_primary_key fhkey; | |
1243 | |
1244 dfh = &dellinkp->dfh; | |
1245 name = LN_NAME(dellinkp); | |
1246 /* If the deleted link was not the head of the list, we are done */ | |
1247 if (memcmp(&fhrecp->dfh, dfh, sizeof (*dfh)) || | |
1248 strcmp(fhrecp->name, name)) { | |
1249 /* should never be here... */ | |
1250 if (debug > 1) { | |
1251 (void) fprintf(stderr, | |
1252 "db_update_primary_new_head: primary " | |
1253 "is for [%s,", name); | |
1254 debug_opaque_print(stderr, (void *)dfh, sizeof (*dfh)); | |
1255 (void) fprintf(stderr, "], not [%s,", fhrecp->name); | |
1256 debug_opaque_print(stderr, (void *)&fhrecp->dfh, | |
1257 sizeof (fhrecp->dfh)); | |
1258 (void) fprintf(stderr, "]\n"); | |
1259 } | |
1260 return (0); /* not head of list so done */ | |
1261 } | |
1262 /* Set the head to nextkey if exists. Otherwise, mark file as deleted */ | |
1263 bcopy(&fhrecp->fh.fh_data, fhkey, fhrecp->fh.fh_len); | |
1264 if (nextlinkp == NULL) { | |
1265 /* last link */ | |
1266 /* remove primary record from database */ | |
1267 (void) delete_record(dbp, | |
1268 fhkey, fhrecp->fh.fh_len, | |
1269 "db_update_primary_new_head: fh delete"); | |
1270 return (0); | |
1271 } else { | |
1272 /* | |
1273 * There are still "live" links, so update the primary record. | |
1274 */ | |
1275 next_name = LN_NAME(nextlinkp); | |
1276 fhrecp->reclen = ROUNDUP32(offsetof(fhlist_ent, name) + | |
1277 strlen(next_name) + 1); | |
1278 /* Replace link data with the info for the next link */ | |
1279 (void) memcpy(&fhrecp->dfh, &nextlinkp->dfh, | |
1280 sizeof (nextlinkp->dfh)); | |
1281 (void) strcpy(fhrecp->name, next_name); | |
1282 } | |
1283 /* not last link */ | |
1284 fhrecp->mtime = time(0); | |
1285 fhrecp->atime = fhrecp->mtime; | |
1286 error = store_record(dbp, | |
1287 fhkey, fhrecp->fh.fh_len, fhrecp, | |
1288 fhrecp->reclen, "db_update_primary_new_head: fh"); | |
1289 return (error); | |
1290 } | |
1291 | |
1292 /* | |
1293 * Exported functions | |
1294 */ | |
1295 | |
1296 /* | |
1297 * db_add - add record to the database. If dfh, fh and name are all here, | |
1298 * add both primary and secondary records. If fh is not available, don't | |
1299 * add anything... | |
1300 * Assumes this is a new file, not yet in the database and that the record | |
1301 * for fh is already in. | |
1302 * Return 0 for success, error code otherwise. | |
1303 */ | |
1304 int | |
1305 db_add(char *fhpath, fhandle_t *dfh, char *name, fhandle_t *fh, uint_t flags) | |
1306 { | |
1307 struct db_list *dbp = NULL; | |
1308 fhlist_ent fhrec, *fhrecp; | |
1309 int error = 0; | |
1310 | |
1311 if (fh == NULL) { | |
1312 /* nothing to add */ | |
1313 return (EINVAL); | |
1314 } | |
1315 if (fh == &public_fh) { | |
1316 dbp = db_get_all_databases(fhpath, FALSE); | |
1317 } else { | |
1318 dbp = db_get_db(fhpath, &fh->fh_fsid, &error, O_CREAT); | |
1319 } | |
1320 for (; dbp != NULL; dbp = ((fh != &public_fh) ? NULL : dbp->next)) { | |
1321 if (debug > 3) { | |
1322 (void) printf("db_add: name '%s', db '%s'\n", | |
1323 name, dbp->path); | |
1324 } | |
1325 fhrecp = db_add_primary(dbp, dfh, name, fh, flags, | |
1326 &fhrec, &error); | |
1327 if (fhrecp == NULL) { | |
1328 continue; | |
1329 } | |
1330 if ((dfh == NULL) || (name == NULL)) { | |
1331 /* Can't add link information */ | |
1332 syslog(LOG_ERR, gettext( | |
1333 "db_add: dfh %p, name %p - invalid"), | |
1334 (void *)dfh, (void *)name); | |
1335 error = EINVAL; | |
1336 continue; | |
1337 } | |
1338 if (fh == &public_fh) { | |
1339 while ((fhrecp != NULL) && strcmp(name, fhrecp->name)) { | |
1340 /* Replace the public fh rather than add link */ | |
1341 error = db_delete_link(fhpath, dfh, | |
1342 fhrecp->name); | |
1343 fhrecp = db_add_primary(dbp, dfh, name, fh, | |
1344 flags, &fhrec, &error); | |
1345 } | |
1346 if (fhrecp == NULL) { | |
1347 continue; | |
1348 } | |
1349 } | |
1350 error = db_add_secondary(dbp, dfh, name, fh, fhrecp); | |
1351 if (fhrecp != &fhrec) { | |
1352 free(fhrecp); | |
1353 } | |
1354 } | |
1355 return (error); | |
1356 } | |
1357 | |
1358 /* | |
1359 * db_lookup - search the database for the file identified by fh. | |
1360 * Return the entry in *fhrecpp if found, or NULL with error set otherwise. | |
1361 */ | |
1362 fhlist_ent * | |
1363 db_lookup(char *fhpath, fhandle_t *fh, fhlist_ent *fhrecp, int *errorp) | |
1364 { | |
1365 struct db_list *dbp; | |
1366 fh_primary_key fhkey; | |
1367 | |
1368 if ((fhpath == NULL) || (fh == NULL) || (errorp == NULL)) { | |
1369 if (errorp != NULL) | |
1370 *errorp = EINVAL; | |
1371 return (NULL); | |
1372 } | |
1373 *errorp = 0; | |
1374 if (fh == &public_fh) { | |
1375 dbp = db_get_all_databases(fhpath, FALSE); | |
1376 } else { | |
1377 dbp = db_get_db(fhpath, &fh->fh_fsid, errorp, O_CREAT); | |
1378 } | |
1379 if (dbp == NULL) { | |
1380 /* Could not get or create database */ | |
1381 return (NULL); | |
1382 } | |
1383 bcopy(&fh->fh_data, fhkey, fh->fh_len); | |
1384 fhrecp = fetch_record(dbp, fhkey, fh->fh_len, fhrecp, | |
1385 errorp, "db_lookup"); | |
1386 /* Update fhrec atime if needed */ | |
1387 if (fhrecp != NULL) { | |
1388 *errorp = db_update_fhrec(dbp, fhkey, fh->fh_len, fhrecp, | |
1389 "db_lookup"); | |
1390 } | |
1391 return (fhrecp); | |
1392 } | |
1393 | |
1394 /* | |
1395 * db_lookup_link - search the database for the file identified by (dfh,name). | |
1396 * If the link was found, use it to search for the primary record. | |
1397 * Return 0 and set the entry in *fhrecpp if found, return error otherwise. | |
1398 */ | |
1399 fhlist_ent * | |
1400 db_lookup_link(char *fhpath, fhandle_t *dfh, char *name, fhlist_ent *fhrecp, | |
1401 int *errorp) | |
1402 { | |
1403 struct db_list *dbp; | |
1404 fh_secondary_key linkkey; | |
1405 linkinfo_ent *linkp; | |
1406 int linksize, fhkeysize; | |
1407 char *fhkey; | |
1408 | |
1409 if ((fhpath == NULL) || (dfh == NULL) || (name == NULL) || | |
1410 (errorp == NULL)) { | |
1411 if (errorp != NULL) | |
1412 *errorp = EINVAL; | |
1413 return (NULL); | |
1414 } | |
1415 *errorp = 0; | |
1416 if (dfh == &public_fh) { | |
1417 dbp = db_get_all_databases(fhpath, FALSE); | |
1418 } else { | |
1419 dbp = db_get_db(fhpath, &dfh->fh_fsid, errorp, O_CREAT); | |
1420 } | |
1421 if (dbp == NULL) { | |
1422 /* Could not get or create database */ | |
1423 return (NULL); | |
1424 } | |
1425 /* Get the link record */ | |
1426 linksize = fill_link_key(linkkey, dfh, name); | |
1427 linkp = fetch_record(dbp, linkkey, linksize, NULL, errorp, | |
1428 "db_lookup_link link"); | |
1429 if (linkp != NULL) { | |
1430 /* Now use link to search for fh entry */ | |
1431 fhkeysize = LN_FHKEY_LEN(linkp); | |
1432 fhkey = LN_FHKEY(linkp); | |
1433 fhrecp = fetch_record(dbp, fhkey, fhkeysize, | |
1434 (void *)fhrecp, errorp, "db_lookup_link fh"); | |
1435 /* Update fhrec atime if needed */ | |
1436 if (fhrecp != NULL) { | |
1437 *errorp = db_update_fhrec(dbp, fhkey, fhkeysize, fhrecp, | |
1438 "db_lookup_link fhrec"); | |
1439 } | |
1440 /* Update link atime if needed */ | |
1441 *errorp = db_update_linkinfo(dbp, linkkey, linksize, linkp, | |
1442 "db_lookup_link link"); | |
1443 free(linkp); | |
1444 } else { | |
1445 fhrecp = NULL; | |
1446 } | |
1447 return (fhrecp); | |
1448 } | |
1449 | |
1450 /* | |
1451 * delete_link - delete the requested link from the database. If it's the | |
1452 * last link in the database for that file then remove the primary record | |
1453 * as well. *errorp contains the returned error code. | |
1454 * Return ENOENT if link not in database and 0 otherwise. | |
1455 */ | |
1456 static int | |
1457 delete_link_by_key(struct db_list *dbp, char *linkkey, int *linksizep, | |
1458 int *errorp, char *errstr) | |
1459 { | |
1460 int nextsize, prevsize, fhkeysize, linksize; | |
1461 char *nextkey, *prevkey, *fhkey; | |
1462 linkinfo_ent *dellinkp, *nextlinkp; | |
1463 fhlist_ent *fhrecp, fhrec; | |
1464 | |
1465 *errorp = 0; | |
1466 linksize = *linksizep; | |
1467 /* Get the link record */ | |
1468 dellinkp = fetch_record(dbp, linkkey, linksize, NULL, errorp, errstr); | |
1469 if (dellinkp == NULL) { | |
1470 /* | |
1471 * Link not in database. | |
1472 */ | |
1473 if (debug > 2) { | |
1474 debug_print_key(stderr, errstr, | |
1475 "link not in database\n", | |
1476 linkkey, linksize); | |
1477 } | |
1478 *linksizep = 0; | |
1479 return (ENOENT); | |
1480 } | |
1481 /* | |
1482 * Possibilities: | |
1483 * 1. Normal case - only one link to delete: the link next and | |
1484 * prev should be NULL, and fhrec's name/dfh are same | |
1485 * as the link. Remove the link and fhrec. | |
1486 * 2. Multiple hard links, and the deleted link is the head of | |
1487 * the list. Remove the link and replace the link key in | |
1488 * the primary record to point to the new head. | |
1489 * 3. Multiple hard links, and the deleted link is not the | |
1490 * head of the list (not the same as in fhrec) - just | |
1491 * delete the link and update the previous and next records | |
1492 * in the links linked list. | |
1493 */ | |
1494 | |
1495 /* Get next and prev keys for linked list updates */ | |
1496 nextsize = LN_NEXT_LEN(dellinkp); | |
1497 nextkey = ((nextsize > 0) ? LN_NEXT(dellinkp) : NULL); | |
1498 prevsize = LN_PREV_LEN(dellinkp); | |
1499 prevkey = ((prevsize > 0) ? LN_PREV(dellinkp) : NULL); | |
1500 /* Update the linked list for the file */ | |
1501 nextlinkp = update_linked_list(dbp, nextkey, nextsize, | |
1502 prevkey, prevsize, errorp); | |
1503 if ((nextlinkp == NULL) && (*errorp != 0)) { | |
1504 free(dellinkp); | |
1505 *linksizep = 0; | |
1506 return (0); | |
1507 } | |
1508 /* Delete link record */ | |
1509 *errorp = delete_record(dbp, linkkey, linksize, errstr); | |
1510 /* Get the primary key */ | |
1511 fhkeysize = LN_FHKEY_LEN(dellinkp); | |
1512 fhkey = LN_FHKEY(dellinkp); | |
1513 fhrecp = fetch_record(dbp, fhkey, fhkeysize, | |
1514 &fhrec, errorp, errstr); | |
1515 if (fhrecp == NULL) { | |
1516 /* Should never happen */ | |
1517 if (debug > 1) { | |
1518 debug_print_key(stderr, errstr, | |
1519 "fetch primary for ", linkkey, linksize); | |
1520 (void) fprintf(stderr, " Error %s\n", | |
1521 ((*errorp >= 0) ? strerror(*errorp) : "Unknown")); | |
1522 } | |
1523 } else if ((*errorp == 0) && (prevsize <= 0)) { | |
1524 /* This is the head of the list update primary record */ | |
1525 *errorp = db_update_primary_new_head(dbp, dellinkp, | |
1526 nextlinkp, fhrecp); | |
1527 } else { | |
1528 /* Update fhrec atime if needed */ | |
1529 *errorp = db_update_fhrec(dbp, fhkey, fhkeysize, fhrecp, | |
1530 errstr); | |
1531 } | |
1532 *linksizep = nextsize; | |
1533 if (nextsize > 0) | |
1534 (void) memcpy(linkkey, nextkey, nextsize); | |
1535 if (nextlinkp != NULL) | |
1536 free(nextlinkp); | |
1537 free(dellinkp); | |
1538 return (0); | |
1539 } | |
1540 | |
1541 /* | |
1542 * delete_link - delete the requested link from the database. If it's the | |
1543 * last link in the database for that file then remove the primary record | |
1544 * as well. If nextlinkkey/sizep are non-null, copy the key and key size of | |
1545 * the next link in the chain into them (this would save a dbm_fetch op). | |
1546 * Return ENOENT if link not in database and 0 otherwise, with *errorp | |
1547 * containing the returned error if any from the delete_link ops. | |
1548 */ | |
1549 static int | |
1550 delete_link(struct db_list *dbp, fhandle_t *dfh, char *name, | |
1551 char *nextlinkkey, int *nextlinksizep, int *errorp, char *errstr) | |
1552 { | |
1553 int linkerr; | |
1554 | |
1555 *errorp = 0; | |
1556 if ((nextlinkkey != NULL) && (nextlinksizep != NULL)) { | |
1557 *nextlinksizep = fill_link_key(nextlinkkey, dfh, name); | |
1558 linkerr = delete_link_by_key(dbp, nextlinkkey, nextlinksizep, | |
1559 errorp, errstr); | |
1560 } else { | |
1561 int linksize; | |
1562 fh_secondary_key linkkey; | |
1563 | |
1564 linksize = fill_link_key(linkkey, dfh, name); | |
1565 linkerr = delete_link_by_key(dbp, linkkey, &linksize, | |
1566 errorp, errstr); | |
1567 } | |
1568 return (linkerr); | |
1569 } | |
1570 | |
1571 /* | |
1572 * db_delete_link - search the database for the file system for link. | |
1573 * Delete the link from the database. If this is the "primary" link, | |
1574 * set the primary record for the next link. If it's the last one, | |
1575 * delete the primary record. | |
1576 * Return 0 for success, error code otherwise. | |
1577 */ | |
1578 int | |
1579 db_delete_link(char *fhpath, fhandle_t *dfh, char *name) | |
1580 { | |
1581 struct db_list *dbp; | |
1582 int error = 0; | |
1583 | |
1584 if ((fhpath == NULL) || (dfh == NULL) || (name == NULL)) { | |
1585 return (EINVAL); | |
1586 } | |
1587 if (dfh == &public_fh) { | |
1588 dbp = db_get_all_databases(fhpath, TRUE); | |
1589 } else { | |
1590 dbp = db_get_db(fhpath, &dfh->fh_fsid, &error, O_CREAT); | |
1591 } | |
1592 for (; dbp != NULL; dbp = ((dfh == &public_fh) ? dbp->next : NULL)) { | |
1593 (void) delete_link(dbp, dfh, name, NULL, NULL, &error, | |
1594 "db_delete_link link"); | |
1595 } | |
1596 return (error); | |
1597 } | |
1598 | |
1599 #ifdef DEBUG | |
1600 /* | |
1601 * db_delete - Deletes the fhrec corresponding to the fh. Use only | |
1602 * for repairing the fhtable, not for normal handling. | |
1603 * Return 0 for success, error code otherwise. | |
1604 */ | |
1605 int | |
1606 db_delete(char *fhpath, fhandle_t *fh) | |
1607 { | |
1608 struct db_list *dbp; | |
1609 int error = 0; | |
1610 | |
1611 if ((fhpath == NULL) || (fh == NULL)) { | |
1612 return (EINVAL); | |
1613 } | |
1614 if (fh == &public_fh) { | |
1615 dbp = db_get_all_databases(fhpath, TRUE); | |
1616 } else { | |
1617 dbp = db_get_db(fhpath, &fh->fh_fsid, &error, O_CREAT); | |
1618 } | |
1619 for (; dbp != NULL; dbp = ((fh == &public_fh) ? dbp->next : NULL)) { | |
1620 /* Get the link record */ | |
1621 (void) delete_record(dbp, &fh->fh_data, fh->fh_len, | |
1622 "db_delete: fh delete"); | |
1623 } | |
1624 return (error); | |
1625 } | |
1626 #endif /* DEBUG */ | |
1627 | |
1628 /* | |
1629 * db_rename_link - search the database for the file system for link. | |
1630 * Add the new link and delete the old link from the database. | |
1631 * Return 0 for success, error code otherwise. | |
1632 */ | |
1633 int | |
1634 db_rename_link(char *fhpath, fhandle_t *from_dfh, char *from_name, | |
1635 fhandle_t *to_dfh, char *to_name) | |
1636 { | |
1637 int error; | |
1638 struct db_list *dbp; | |
1639 fhlist_ent fhrec, *fhrecp; | |
1640 | |
1641 if ((fhpath == NULL) || (from_dfh == NULL) || (from_name == NULL) || | |
1642 (to_dfh == NULL) || (to_name == NULL)) { | |
1643 return (EINVAL); | |
1644 } | |
1645 if (from_dfh == &public_fh) { | |
1646 dbp = db_get_all_databases(fhpath, FALSE); | |
1647 } else { | |
1648 dbp = db_get_db(fhpath, &from_dfh->fh_fsid, &error, O_CREAT); | |
1649 } | |
1650 for (; dbp != NULL; | |
1651 dbp = ((from_dfh != &public_fh) ? NULL : dbp->next)) { | |
1652 /* find existing link */ | |
1653 fhrecp = db_lookup_link(fhpath, from_dfh, from_name, &fhrec, | |
1654 &error); | |
1655 if (fhrecp == NULL) { | |
1656 /* Could not find the link */ | |
1657 continue; | |
1658 } | |
1659 /* Delete the old link (if last primary record not deleted) */ | |
1660 error = db_delete_link(fhpath, from_dfh, from_name); | |
1661 if (error == 0) { | |
1662 error = db_add(fhpath, to_dfh, to_name, &fhrecp->fh, | |
1663 fhrecp->flags); | |
1664 } | |
1665 } | |
1666 return (error); | |
1667 } | |
1668 | |
1669 /* | |
1670 * db_print_all_keys: prints all keys for a given filesystem. If fsidp is | |
1671 * NULL, print for all filesystems covered by fhpath. | |
1672 */ | |
1673 void | |
1674 db_print_all_keys(char *fhpath, fsid_t *fsidp, FILE *fp) | |
1675 { | |
1676 struct db_list *dbp; | |
1677 datum key; | |
1678 int error, len; | |
1679 char strkey[NFS_FHMAXDATA + MAXNAMELEN]; | |
1680 db_record rec; | |
1681 void *ptr; | |
1682 | |
1683 if ((fhpath == NULL) || | |
1684 ((fsidp != NULL) && (fsidp == &public_fh.fh_fsid))) | |
1685 return; | |
1686 if (fsidp == NULL) { | |
1687 (void) db_get_all_databases(fhpath, TRUE); | |
1688 dbp = db_fs_list; | |
1689 } else { | |
1690 dbp = db_get_db(fhpath, fsidp, &error, 0); | |
1691 } | |
1692 if (dbp == NULL) { | |
1693 /* Could not get or create database */ | |
1694 return; | |
1695 } | |
1696 len = strlen(fhpath); | |
1697 for (; dbp != NULL; dbp = ((fsidp != NULL) ? NULL : dbp->next)) { | |
1698 if (strncmp(fhpath, dbp->path, len)) | |
1699 continue; | |
1700 (void) fprintf(fp, | |
1701 "\nStart print database for fsid 0x%x 0x%x\n", | |
1702 dbp->fsid.val[0], dbp->fsid.val[1]); | |
1703 (void) fprintf(fp, "=============================\n"); | |
1704 for (key = dbm_firstkey(dbp->db); key.dptr != NULL; | |
1705 key = dbm_nextkey(dbp->db)) { | |
1706 (void) memcpy(strkey, key.dptr, key.dsize); | |
1707 debug_print_key(fp, "", "", strkey, key.dsize); | |
1708 if (debug < 2) | |
1709 continue; | |
1710 ptr = fetch_record(dbp, key.dptr, key.dsize, | |
1711 (void *)&rec, &error, "db_prt_keys"); | |
1712 if (ptr == NULL) | |
1713 continue; | |
1714 if (key.dsize == NFS_FHMAXDATA) { | |
1715 /* fhrec */ | |
1716 debug_print_fhlist(fp, &rec.fhlist_rec); | |
1717 } else if (key.dsize > NFS_FHMAXDATA) { | |
1718 /* linkinfo */ | |
1719 debug_print_linkinfo(fp, &rec.link_rec); | |
1720 } | |
1721 (void) fprintf(fp, "-----------------------------\n"); | |
1722 } | |
1723 (void) fprintf(fp, "End print database for fsid 0x%x 0x%x\n", | |
1724 dbp->fsid.val[0], dbp->fsid.val[1]); | |
1725 } | |
1726 } | |
1727 | |
1728 void | |
1729 debug_opaque_print(FILE *fp, void *buf, int size) | |
1730 { | |
1731 int bufoffset = 0; | |
1732 char debug_str[200]; | |
1733 | |
1734 if ((buf == NULL) || (size <= 0)) | |
1735 return; | |
1736 | |
1737 nfslog_opaque_print_buf(buf, size, debug_str, &bufoffset, 200); | |
1738 (void) fprintf(fp, debug_str); | |
1739 } | |
1740 | |
1741 /* | |
1742 * links_timedout() takes a primary records and searches all of its | |
1743 * links to see if they all have access times that are older than | |
1744 * the 'prune_timeout' value. TRUE if all links are old and FALSE | |
1745 * if there is just one link that has an access time which is recent. | |
1746 */ | |
1747 static int | |
1748 links_timedout(struct db_list *pdb, fhlist_ent *pfe, time_t ts) | |
1749 { | |
1750 fh_secondary_key linkkey; | |
1751 linkinfo_ent *linkp, link_st; | |
1752 int error; | |
1753 int linksize; | |
1754 void *cookie; | |
1755 | |
1756 /* Get the link record */ | |
1757 linksize = fill_link_key(linkkey, &pfe->dfh, pfe->name); | |
1758 cookie = NULL; | |
1759 do { | |
1760 linkp = get_next_link(pdb, linkkey, &linksize, &link_st, | |
1761 &cookie, &error, "links_timedout"); | |
1762 if ((linkp != NULL) && | |
1763 (difftime(ts, linkp->atime) <= prune_timeout)) { | |
1764 /* update primary record to have an uptodate time */ | |
1765 pfe = fetch_record(pdb, (void *)&pfe->fh.fh_data, | |
1766 pfe->fh.fh_len, NULL, &error, | |
1767 "links_timedout"); | |
1768 if (pfe == NULL) { | |
1769 syslog(LOG_ERR, gettext( | |
1770 "links_timedout: fetch fhrec error %s\n"), | |
1771 strerror(error)); | |
1772 } else { | |
1773 if (difftime(pfe->atime, linkp->atime) < 0) { | |
1774 /* update fhrec atime */ | |
1775 pfe->atime = linkp->atime; | |
1776 (void) store_record(pdb, | |
1777 (void *)&pfe->fh.fh_data, | |
1778 pfe->fh.fh_len, pfe, | |
1779 pfe->reclen, "links_timedout"); | |
1780 } | |
1781 free(pfe); | |
1782 } | |
1783 free_link_cookies(cookie); | |
1784 return (FALSE); | |
1785 } | |
1786 } while (linksize > 0); | |
1787 | |
1788 free_link_cookies(cookie); | |
1789 return (TRUE); | |
1790 } | |
1791 | |
1792 /* | |
1793 * prune_dbs() will search all of the open databases looking for records | |
1794 * that have not been accessed in the last 'prune_timeout' seconds. | |
1795 * This search is done on the primary records and a list of potential | |
1796 * timeout candidates is built. The reason for doing this is to not | |
1797 * disturb the underlying dbm_firstkey()/dbm_nextkey() sequence; we | |
1798 * want to search all of the records in the database. | |
1799 * Once we have our candidate list built, we examine each of those | |
1800 * item's links to check if the links have been accessed within the | |
1801 * 'prune_timeout' seconds. If neither the primary nor any its links | |
1802 * have been accessed, then all of those records are removed/deleted | |
1803 * from the database. | |
1804 */ | |
1805 int | |
1806 prune_dbs(char *fhpath) | |
1807 { | |
1808 struct db_list *pdb; | |
1809 datum key; | |
1810 db_record *ptr; | |
1811 struct fhlist_ent *pfe; | |
1812 int error, linkerr, linksize; | |
1813 time_t cur_time = time(0); | |
1814 fh_secondary_key linkkey; | |
1815 struct thelist { | |
1816 struct thelist *next; | |
1817 db_record *ptr; | |
1818 } thelist, *ptl; | |
1819 int cnt = 0; | |
1820 | |
1821 if (fhpath != NULL) | |
1822 (void) db_get_all_databases(fhpath, TRUE); | |
1823 | |
1824 thelist.next = NULL; | |
1825 /* | |
1826 * Search each of the open databases | |
1827 */ | |
1828 for (pdb = db_fs_list; pdb; pdb = pdb->next) { | |
1829 do { | |
1830 /* Check each record in the database */ | |
1831 for (key = dbm_firstkey(pdb->db); key.dptr != NULL; | |
1832 key = dbm_nextkey(pdb->db)) { | |
1833 /* We're only interested in primary records */ | |
1834 if (key.dsize != NFS_FHMAXDATA) | |
1835 continue; /* probably a link record */ | |
1836 ptr = fetch_record(pdb, key.dptr, key.dsize, | |
1837 NULL, &error, "dump_db"); | |
1838 if (ptr == NULL) | |
1839 continue; | |
1840 /* | |
1841 * If this record is a primary record and it is | |
1842 * not an export point or a public file handle path, | |
1843 * check it for a ancient access time. | |
1844 */ | |
1845 if ((ptr->fhlist_rec.flags & | |
1846 (EXPORT_POINT | PUBLIC_PATH)) || | |
1847 (difftime(cur_time, ptr->fhlist_rec.atime) <= | |
1848 prune_timeout)) { | |
1849 /* Keep this record in the database */ | |
1850 free(ptr); | |
1851 } else { | |
1852 /* Found one? Save off info about it */ | |
1853 ptl = malloc(sizeof (struct thelist)); | |
1854 if (ptl == NULL) { | |
1855 syslog(LOG_ERR, gettext( | |
1856 "prune_dbs: malloc failed, error %s\n"), | |
1857 strerror(errno)); | |
1858 break; | |
1859 } | |
1860 ptl->ptr = ptr; | |
1861 ptl->next = thelist.next; | |
1862 thelist.next = ptl; | |
1863 cnt++; /* count how many records allocated */ | |
1864 if (cnt > MAX_PRUNE_REC_CNT) { | |
1865 /* Limit number of records malloc'd */ | |
1866 if (debug) | |
1867 (void) fprintf(stderr, | |
1868 "prune_dbs: halt search - too many records\n"); | |
1869 break; | |
1870 } | |
1871 } | |
1872 } | |
1873 | |
1874 /* | |
1875 * Take the saved records and check their links to make | |
1876 * sure that they have not been accessed as well. | |
1877 */ | |
1878 for (ptl = thelist.next; ptl; ptl = thelist.next) { | |
1879 thelist.next = ptl->next; | |
1880 /* Everything timed out? */ | |
1881 pfe = &(ptl->ptr->fhlist_rec); | |
1882 if (links_timedout(pdb, pfe, cur_time)) { | |
1883 | |
1884 /* | |
1885 * Iterate until we run out of links. | |
1886 * We have to do this since there can be | |
1887 * multiple links to a primary record and | |
1888 * we need to delete one at a time. | |
1889 */ | |
1890 /* Delete the link and get the next */ | |
1891 linkerr = delete_link(pdb, | |
1892 &pfe->dfh, pfe->name, linkkey, | |
1893 &linksize, &error, "dump_db"); | |
1894 while ((linksize > 0) && !(error || linkerr)) { | |
1895 /* Delete the link and get the next */ | |
1896 linkerr = delete_link_by_key(pdb, | |
1897 linkkey, &linksize, | |
1898 &error, "dump_db"); | |
1899 if (error || linkerr) { | |
1900 break; | |
1901 } | |
1902 } | |
1903 if (linkerr) { | |
1904 /* link not in database, primary is */ | |
1905 /* Should never happen */ | |
1906 if (debug > 1) { | |
1907 (void) fprintf(stderr, | |
1908 "prune_dbs: Error primary exists "); | |
1909 debug_opaque_print(stderr, | |
1910 (void *)&pfe->fh, | |
1911 sizeof (pfe->fh)); | |
1912 (void) fprintf(stderr, "\n"); | |
1913 } | |
1914 if (debug) | |
1915 syslog(LOG_ERR, gettext( | |
1916 "prune_dbs: Error primary exists\n")); | |
1917 (void) delete_record(pdb, | |
1918 &pfe->fh.fh_data, pfe->fh.fh_len, | |
1919 "prune_dbs: fh delete"); | |
1920 } | |
1921 } | |
1922 /* Make sure to free the pointers used in the list */ | |
1923 free(ptl->ptr); | |
1924 free(ptl); | |
1925 cnt--; | |
1926 } | |
1927 thelist.next = NULL; | |
1928 } while (key.dptr != NULL); | |
1929 } | |
1930 return (0); | |
1931 } |