📄 es_file.c
字号:
written = -2;#ifdef DEBUG (void) fprintf(stderr, "Failed write in %s of %d chars\n", "es_file_flush_write_buf", buf->used);#endif goto Return; } if (buf->start+written > private->length_on_disk) { /* Extending file on disk, so make sure bytes make it. */ if (fsync(private->fd) == -1) { private->status = ES_FSYNC_FAILED;#ifdef DEBUG (void) fprintf(stderr, "Failed fsynch in es_file_flush_write_buf\n");#endif /* BUG ALERT! Who knows what state the file is in? */ written = -3; goto Return; } private->length_on_disk = buf->start+written; } BUF_INVALIDATE(buf);Return: return(written);} static intes_file_move_write_buf(private, include, also_include, include_offset) register Es_file_data private; register Es_index include, also_include; char **include_offset;/* * Caller ensures: * include <= also_include < include+WRITE_BUF_LEN < ES_INFINITY, * include <= private->length * Return values: * < 0 indicate various errors, * = 0 implies write_buf already correctly positioned, * > 0 implies write_buf written then moved. * Assumptions: * if you have to write any bytes, you might as well write them all. * if you have to read any bytes, you might as well read them all. */{ register Es_file_buf buf = &private->write_buf; register Es_file_buf read_buf; register int written = 0; Es_index buf_last_plus_one; if (buf->used == 0) goto Fill_Buffer; buf_last_plus_one = BUF_LAST_PLUS_ONE(buf);#ifdef DEBUG if (buf_last_plus_one > private->length) take_breakpoint();#endif /* Buffer must contain include and also_include. * There are two possible problems, even if include is contained: * 1) include is too far into the buffer for also_include to also be * contained, or * 2) there is space available in the buffer for also_include, but * the buffer needs to fill in bytes from the disk between * its current end and also_include. * If include is at buffer end, or also_include is at or beyond * buffer end, it may be possible to simply extend the buffer iff * buffer is at the end of the stream and it has room left. */ if (include < buf->start || include > buf_last_plus_one || (include == buf_last_plus_one && include >= buf->start+WRITE_BUF_LEN) || (also_include >= buf_last_plus_one && (buf_last_plus_one < private->length || also_include >= buf->start+WRITE_BUF_LEN)) ) { written = es_file_flush_write_buf(private, &private->write_buf); if (written < 0) goto Return; /* Possible future optimizations: * 1) Deal with buf_last_plus_one <= also_include * < buf->start+WRITE_BUF_LEN * 2) Slide buffer around to avoid full read when possible * 3) Copy from read_buf to avoid full read when possible */Fill_Buffer: if (es_file_fill_buf(private, buf, include, include+WRITE_BUF_LEN > private->length_on_disk ? private->length_on_disk : include+WRITE_BUF_LEN) < 0) { written = -4; goto Return; } } *include_offset = buf->chars + (include - buf->start);Return: return(written);} static voides_file_maybe_truncate_buf(buf, new_last_plus_one) register Es_file_buf buf; register Es_index new_last_plus_one;{ if (buf->used > 0 && new_last_plus_one < BUF_LAST_PLUS_ONE(buf)) { buf->used = (new_last_plus_one < buf->start) ? 0 : new_last_plus_one - buf->start; }} static Es_statuses_file_commit(esh) Es_handle esh;{ register Es_file_data private = ABS_TO_REP(esh); if (es_file_flush_write_buf(private, &private->write_buf) < 0) { return(private->status); } private->flags |= COMMIT_DONE; return(ES_SUCCESS);} static Es_handlees_file_destroy(esh) Es_handle esh;{ register Es_file_data private = ABS_TO_REP(esh); if (private->write_buf.chars) {#ifdef DEBUG if ((private->write_buf.used > 0) && (private->flags & COMMIT_DONE)) { /* Caller should have called es_commit in order to guarantee * appropriate recovery in case of errors. */ take_breakpoint(); }#endif free(private->write_buf.chars); } (void) close(private->fd); private->fd = -1; if ((private->options & ES_OPT_APPEND) && (private->flags & COMMIT_DONE) == 0) { (void) unlink(private->name); } free((char *)esh); free(private->read_buf.chars);#ifndef BACKUP_AT_HEAD_OF_LINK free(private->true_name);#endif free(private->name); free((char *)private); return(NULL);}static Es_indexes_file_get_length(esh) Es_handle esh; { register Es_file_data private = ABS_TO_REP(esh); return(private->length);} static Es_indexes_file_get_position(esh) Es_handle esh; { register Es_file_data private = ABS_TO_REP(esh); return(private->pos);} static Es_indexes_file_set_position(esh, pos) Es_handle esh; register Es_index pos;{ register Es_file_data private = ABS_TO_REP(esh); if (pos > private->length) pos = private->length; private->pos = pos; return(private->pos);} static Es_indexes_file_read(esh, count, buf, count_read) Es_handle esh; int count; register int *count_read; caddr_t buf;/* * Needed characters may be in the read_buf, the write_buf, or on disk, or * some combination of all three. */{ register Es_file_data private = ABS_TO_REP(esh); register Es_index pos = private->pos, lpo; register int to_read, still_needed; es_file_buf dummy_read_buf; char *offset; /* Client may request more bytes than are available, so count cannot * be trusted in the following code. */ *count_read = (count > private->length - pos) ? (private->length - pos) : count; for (still_needed = *count_read; still_needed > 0; still_needed -= to_read, pos += to_read) { /* Figure out where the next set of characters is coming from. * The write_buf has precedence over the read_buf in the tests * so that overlap range reads from the write_buf! */ if (BUF_CONTAINS_POS(&private->write_buf, pos)) { to_read = BUF_LAST_PLUS_ONE(&private->write_buf) - pos; if (to_read > still_needed) to_read = still_needed; bcopy(private->write_buf.chars+(pos-private->write_buf.start), buf + (*count_read-still_needed), to_read); } else if (BUF_CONTAINS_POS(&private->read_buf, pos)) { to_read = BUF_LAST_PLUS_ONE(&private->read_buf) - pos; if (to_read > still_needed) to_read = still_needed; bcopy(private->read_buf.chars+(pos-private->read_buf.start), buf + (*count_read-still_needed), to_read); } else if (still_needed <= READ_BUF_LEN) { /* Since we have to read from disk, might as well get as * many characters as possible. */ lpo = pos + READ_BUF_LEN; if (lpo > private->length_on_disk) lpo = private->length_on_disk; /* Overlap with write_buf is avoided for good hygiene. */ if (private->write_buf.used > 0 && pos < private->write_buf.start && lpo > private->write_buf.start) lpo = private->write_buf.start; if (es_file_fill_buf(private, &private->read_buf, pos, lpo) < 0) { *count_read = 0; pos = private->pos; goto Return; } /* Go around again and characters will get copied * from read_buf in earlier part of loop. */ to_read = 0; } else { /* Read directly from the disk */ dummy_read_buf.chars = buf + (*count_read-still_needed); lpo = pos + still_needed; if (lpo > private->length_on_disk) lpo = private->length_on_disk; /* Overlap with write_buf is forbidden. */ if (private->write_buf.used > 0 && lpo > private->write_buf.start) lpo = private->write_buf.start; if (es_file_fill_buf(private, &dummy_read_buf, pos, lpo) < 0) { *count_read = 0; pos = private->pos; goto Return; } to_read = dummy_read_buf.used; } }Return: private->pos = pos; return(pos);}/* Following enumeration details the three possible types of replace. */typedef enum {esfr_truncate, esfr_overwrite, esfr_insert} Esfr_mode; static Es_indexes_file_replace(esh, last_plus_one, count, buf, count_used) Es_handle esh; register int count; int *count_used, last_plus_one; caddr_t buf; { register Es_file_data private = ABS_TO_REP(esh); register Esfr_mode mode; char *offset; es_file_buf dummy_write_buf; /* Ensure that the operation is consistent with the options. */ if ((private->options & ES_OPT_APPEND) == 0) { private->status = ES_INCONSISTENT_POS;#ifdef DEBUG (void) fprintf(stderr, "es_file_replace: read-only stream\n");#endif goto Error_Return; } if (private->pos < private->length) { if (last_plus_one <= private->length) { mode = esfr_overwrite; } else { mode = esfr_truncate; if (count != 0) { private->status = ES_INVALID_ARGUMENTS;#ifdef DEBUG (void) fprintf(stderr, "%s: non-zero (%d) count in truncate\n", "es_file_replace", count);#endif goto Error_Return; } if (private->pos < private->length_on_disk) { private->status = ES_INVALID_ARGUMENTS;#ifdef DEBUG (void) fprintf(stderr, "%s: truncate @ %d when length_on_disk %d\n", "es_file_replace", private->pos, private->length_on_disk);#endif goto Error_Return; } } if ((private->options & ES_OPT_OVERWRITE) == 0 || ((mode == esfr_overwrite) && (count != last_plus_one-private->pos))) { private->status = ES_INVALID_ARGUMENTS;#ifdef DEBUG (void) fprintf(stderr, "%s last_plus_one is %d, len is %d\n", "es_file_replace position error:", last_plus_one, private->length); #endif goto Error_Return; } } else { mode = esfr_insert; } /* Do the replace */ if (mode == esfr_truncate) { es_file_maybe_truncate_buf(&private->read_buf); es_file_maybe_truncate_buf(&private->write_buf); *count_used = 0; } else if (mode == esfr_overwrite) { /* If new bytes will fit in the write_buf, position the write_buf * to accomodate them (unless there are 4 or fewer bytes) and * overwrite. Otherwise, flush out the write_buf and write * the new bytes directly. */ if (count <= WRITE_BUF_LEN) { if (count < 5 && (last_plus_one < private->write_buf.start || private->pos >= BUF_LAST_PLUS_ONE(&private->write_buf))) { goto Write_Direct; } if (es_file_move_write_buf(private, private->pos, last_plus_one, &offset) < 0) { goto Error_Return; } bcopy(buf, offset, count); *count_used = count; } else { goto Flush_And_Write_Direct; } } else /* mode == es_insert */ { /* Insert only happens at end-of-stream, thus it always increases * private->write_buf.used by the size of the insertion. * If new bytes will fit in the write_buf, position the write_buf * to accomodate them and add them. Otherwise, flush out the * write_buf and write the new bytes directly. */ if (count <= WRITE_BUF_LEN) { if (es_file_move_write_buf(private, private->pos, private->pos+count, &offset) < 0) { goto Error_Return; } bcopy(buf, offset, count); private->write_buf.used += count; } else {Flush_And_Write_Direct: if (es_file_flush_write_buf(private, &private->write_buf) < 0) { goto Error_Return; }Write_Direct: /* Fake up a write buffer */ dummy_write_buf.used = count; dummy_write_buf.start = private->pos; dummy_write_buf.chars = buf; if (es_file_flush_write_buf(private, &dummy_write_buf) <= 0) { goto Error_Return; } /* Correct overlap with the read_buf that is not masked * by overlap with the write_buf. */ if (private->read_buf.used > 0 && private->pos < BUF_LAST_PLUS_ONE(&private->read_buf) && private->read_buf.start < private->pos+count) { /* There is overlap: in future, might be better to update * read_buf, but for now, just discard it. */ BUF_INVALIDATE(&private->read_buf); } } *count_used = count; }Return: private->pos += *count_used; if (mode != esfr_overwrite) private->length = private->pos; return(private->pos); Error_Return: return(ES_CANNOT_SET); } extern intes_file_copy_status(esh, to) Es_handle esh; char *to;{ Es_file_data private = ABS_TO_REP(esh); int dummy; return(es_copy_status(to, private->fd, &dummy));}extern Es_handlees_file_make_backup(esh, backup_pattern, status) register Es_handle esh; char *backup_pattern; Es_status *status;/* Currently backup_pattern must be of the form "%s<suffix>" */{ register Es_file_data private; char backup_name[MAXNAMLEN]; int fd, len, retrying = FALSE; Es_status dummy_status; Es_handle result; struct stat stto, stfrom; struct statfs fs; if (status == 0) status = &dummy_status; if ((esh == NULL) || (esh->ops != &es_file_ops)) { *status = ES_INVALID_HANDLE; return(NULL); } *status = ES_CHECK_ERRNO; errno = 0; private = ABS_TO_REP(esh);#ifdef BACKUP_AT_HEAD_OF_LINK (void) sprintf(backup_name, backup_pattern, private->name);#else (void) sprintf(backup_name, backup_pattern, (private->true_name) ? private->true_name : private->name);#endif fd = private->fd; len = lseek(fd, 0L, 1); if (lseek(fd, 0L, 0) != 0) goto Lseek_Failed; if (stat(private->name, &stfrom) < 0) { *status = ES_CHECK_ERRNO; return (ES_NULL); } if (stat(backup_name, &stto) < 0 && errno != ENOENT) { *status = ES_CHECK_ERRNO; return (ES_NULL); } else if (errno == ENOENT) stto.st_size = 0; if (statfs(private->name, &fs) < 0) { *status = ES_CHECK_ERRNO; return (ES_NULL); } if (stfrom.st_size - stto.st_size > fs.f_bavail * DEV_BSIZE) { *status = ES_CHECK_ERRNO; errno = ENOSPC; return (ES_NULL); }Retry: if (es_copy_fd(private->name, backup_name, fd) != 0) { if ((!retrying) && (errno == EACCES)) { /* It may be that the backup_name is already taken by a file * that cannot be overwritten, so try to remove it first. */ if (unlink(backup_name) == 0) { retrying = TRUE; goto Retry; } if (errno == ENOENT) /* backup_name does not exist, so problem with es_copy_fd * really is unfixable access error, which that needs to * be reported to caller, so set errno back! */ errno = EACCES; } return(NULL); } if (lseek(fd, (long)len, 0) != len) goto Lseek_Failed; result = es_file_create(backup_name, 0, status); *status = ES_SUCCESS; return(result);Lseek_Failed: *status = ES_SEEK_FAILED; return(NULL);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -