/* * dir.c * * Copyright (C) 1995 by Volker Lendecke * */ #include #ifdef MODULE #include #include #endif #include #include #include #include #include #include #include #include #include #include "ncplib.h" #define NAME_OFFSET(de) ((int) ((de)->d_name - (char *) (de))) #define ROUND_UP(x) (((x)+3) & ~3) static int ncp_dir_read(struct inode *inode, struct file *filp, char *buf, int count); static int ncp_readdir(struct inode *inode, struct file *filp, void *dirent, filldir_t filldir); static int ncp_read_volume_list(struct ncp_server *server, int start_with, int cache_size); static int ncp_do_readdir(struct ncp_server *server, struct inode *dir, int fpos, int cache_size, struct ncp_dirent *entry); static int get_pname(struct inode *dir, const char *name, int len, char **res_path, int *res_len); static int get_pname_static(struct inode *dir, const char *name, int len, char *path, int *res_len); static struct inode * ncp_iget(struct inode *dir, char *path, struct ncp_dirent *finfo); static void put_pname(char *path); static struct ncp_inode_info * ncp_find_inode(struct ncp_server *server, const char *path); static int ncp_lookup(struct inode *dir, const char *__name, int len, struct inode **result); static int ncp_create(struct inode *dir, const char *name, int len, int mode, struct inode **result); static int ncp_mkdir(struct inode *dir, const char *name, int len, int mode); static int ncp_rmdir(struct inode *dir, const char *name, int len); static int ncp_unlink(struct inode *dir, const char *name, int len); static int ncp_rename(struct inode *old_dir, const char *old_name, int old_len, struct inode *new_dir, const char *new_name, int new_len); static int date_dos2unix(unsigned short time,unsigned short date); /* static void date_unix2dos(int unix_date,unsigned short *time, unsigned short *date); */ static inline void str_upper(char *name) { while (*name) { if (*name >= 'a' && *name <= 'z') *name -= ('a' - 'A'); name++; } } static inline void str_lower(char *name) { while (*name) { if (*name >= 'A' && *name <= 'Z') *name += ('a' - 'A'); name ++; } } static struct file_operations ncp_dir_operations = { NULL, /* lseek - default */ ncp_dir_read, /* read - bad */ NULL, /* write - bad */ ncp_readdir, /* readdir */ NULL, /* select - default */ ncp_ioctl, /* ioctl - default */ NULL, /* mmap */ NULL, /* no special open code */ NULL, /* no special release code */ NULL /* fsync */ }; struct inode_operations ncp_dir_inode_operations = { &ncp_dir_operations, /* default directory file ops */ ncp_create, /* create */ ncp_lookup, /* lookup */ NULL, /* link */ ncp_unlink, /* unlink */ NULL, /* symlink */ ncp_mkdir, /* mkdir */ ncp_rmdir, /* rmdir */ NULL, /* mknod */ ncp_rename, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ NULL, /* bmap */ NULL, /* truncate */ NULL, /* permission */ NULL /* smap */ }; static int ncp_dir_read(struct inode *inode, struct file *filp, char *buf, int count) { return -EISDIR; } /* In ncpfs, we have unique inodes across all mounted filesystems, for all inodes that are in memory. That's why it's enough to index the directory cache by the inode number. */ static unsigned long c_ino = 0; static int c_size; static int c_seen_eof; static int c_last_returned_index; static struct ncp_dirent* c_entry = NULL; static int ncp_readdir(struct inode *inode, struct file *filp, void *dirent, filldir_t filldir) { int result, i = 0; int index = 0; struct ncp_dirent *entry = NULL; struct ncp_server *server = NCP_SERVER(inode); DDPRINTK("ncp_readdir: filp->f_pos = %d\n", (int)filp->f_pos); DDPRINTK("ncp_readdir: inode->i_ino = %ld, c_ino = %ld\n", inode->i_ino, c_ino); if (!inode || !S_ISDIR(inode->i_mode)) { printk("ncp_readdir: inode is NULL or not a directory\n"); return -EBADF; } if (c_entry == NULL) { i = sizeof (struct ncp_dirent) * NCP_READDIR_CACHE_SIZE; c_entry = (struct ncp_dirent *) ncp_kmalloc(i, GFP_KERNEL); if (c_entry == NULL) { printk("ncp_readdir: no MEMORY for cache\n"); return -ENOMEM; } for (i = 0; i < NCP_READDIR_CACHE_SIZE; i++) { c_entry[i].path = (char *) ncp_kmalloc(NCP_MAXNAMELEN + 1, GFP_KERNEL); if (c_entry[i].path == NULL) { DPRINTK("ncp_readdir: could not alloc path\n"); while (--i>=0) kfree(c_entry[i].path); kfree(c_entry); c_entry = NULL; return -ENOMEM; } } } if (filp->f_pos == 0) { ncp_invalid_dir_cache(inode->i_ino); } if (inode->i_ino == c_ino) { for (i = 0; i < c_size; i++) { if (filp->f_pos == c_entry[i].f_pos) { entry = &c_entry[i]; c_last_returned_index = i; index = i; break; } } if ((entry == NULL) && c_seen_eof) return 0; } if (entry == NULL) { DPRINTK("ncp_readdir: Not found in cache.\n"); if (inode->i_ino == (int)&(server->root)) { result = ncp_read_volume_list(server, filp->f_pos, NCP_READDIR_CACHE_SIZE); DPRINTK("ncp_read_volume_list returned %d\n", result); } else { result = ncp_do_readdir(server, inode, filp->f_pos, NCP_READDIR_CACHE_SIZE, c_entry); DPRINTK("ncp_readdir returned %d\n", result); } if (result < 0) { c_ino = 0; return result; } if (result > 0) { c_seen_eof = (result < NCP_READDIR_CACHE_SIZE); c_ino = inode->i_ino; c_size = result; entry = c_entry; c_last_returned_index = 0; index = 0; for (i = 0; i < c_size; i++) { str_lower(c_entry[i].path); } } } if (entry == NULL) { /* Nothing found, even from a ncp call */ return 0; } while (index < c_size) { /* We found it. For getwd(), we have to return the correct inode in d_ino if the inode is currently in use. Otherwise the inode number does not matter. (You can argue a lot about this..) */ int path_len; int len; struct ncp_inode_info *ino_info; char complete_path[NCP_MAXPATHLEN]; len = strlen(entry->path); if ((result = get_pname_static(inode, entry->path, len, complete_path, &path_len)) < 0) return result; ino_info = ncp_find_inode(server, complete_path); /* Some programs seem to be confused about a zero inode number, so we set it to one. Thanks to Gordon Chaffee for this one. */ if (ino_info == NULL) { ino_info = (struct ncp_inode_info *) 1; } DDPRINTK("ncp_readdir: entry->path = %s\n", entry->path); DDPRINTK("ncp_readdir: entry->f_pos = %ld\n", entry->f_pos); if (filldir(dirent, entry->path, len, entry->f_pos, (ino_t)ino_info) < 0) { break; } if ( (inode->i_ino != c_ino) || (entry->f_pos != filp->f_pos)) { /* Someone has destroyed the cache while we slept in filldir */ break; } filp->f_pos += 1; index += 1; entry += 1; } return 0; } static int ncp_read_volume_list(struct ncp_server *server, int fpos, int cache_size) { struct ncp_dirent *entry = c_entry; int total_count = 0; int i; void fill_one(char *name) { if (total_count < fpos) { DPRINTK("ncp_do_readdir: skipped file: %s\n", name); } else if (total_count >= fpos + cache_size) { return; } else { DPRINTK("ncp_do_readdir: found file: %s\n", name); entry->attr = aDIR; entry->mtime = 0; entry->ctime = 0; entry->atime = 0; entry->size = 1024; entry->f_pos = total_count; strcpy(entry->path, name); entry += 1; } total_count += 1; } for (i=0; i 0) { fill_one(info.volume_name); } } fill_one("."); fill_one(".."); return (total_count - fpos); } static int ncp_do_readdir(struct ncp_server *server, struct inode *dir, int fpos, int cache_size, struct ncp_dirent *entry) { struct ncp_filesearch_info fsinfo; struct ncp_file_info finfo; int total_count = 0; void fill_one(int attr, char *name, int upd_time, int upd_date, int cr_date, int ac_date, int length) { if (total_count < fpos) { DPRINTK("ncp_do_readdir: skipped file: %s\n", name); } else if (total_count >= fpos + cache_size) { return; } else { DPRINTK("ncp_do_readdir: found file: %s\n", name); entry->attr = attr; if ((attr & aDIR) == 0) { entry->mtime = date_dos2unix(upd_time, upd_date); entry->ctime = date_dos2unix(0, cr_date); entry->atime = date_dos2unix(0, ac_date); entry->size = length; } else { /* Sorry, I do not know how to get the * values for directories :-(. Mail * lendecke@namu01.gwdg.de if you * know more. */ entry->mtime = 0; entry->ctime = 0; entry->atime = 0; entry->size = 1024; } entry->f_pos = total_count; strcpy(entry->path, name); entry += 1; } total_count += 1; } void doit(int attr) { if (ncp_file_search_init(server, 0, NCP_FINFO(dir)->path, &fsinfo) != 0) { DPRINTK("could not fs init\n"); return; } while (ncp_file_search_continue(server, &fsinfo, attr, "*", &finfo) == 0) { fill_one(attr, finfo.file_name, finfo.update_time, finfo.update_date, finfo.creation_date, finfo.access_date, finfo.file_length); } return; } fill_one(aDIR, ".", 0, 0, 0, 0, 0); fill_one(aDIR, "..", 0, 0, 0, 0, 0); doit(0); doit(aDIR); return (total_count - fpos); } void ncp_init_dir_cache(void) { c_ino = 0; c_entry = NULL; } void ncp_invalid_dir_cache(unsigned long ino) { if (ino == c_ino) { c_ino = 0; c_seen_eof = 0; } } void ncp_free_dir_cache(void) { int i; DPRINTK("ncp_free_dir_cache: enter\n"); if (c_entry == NULL) return; for (i = 0; i < NCP_READDIR_CACHE_SIZE; i++) { ncp_kfree_s(c_entry[i].path, NAME_MAX + 1); } ncp_kfree_s(c_entry, sizeof(struct ncp_dirent) * NCP_READDIR_CACHE_SIZE); c_entry = NULL; DPRINTK("ncp_free_dir_cache: exit\n"); } /* get_pname_static: it expects the res_path to be a preallocated string of len NCP_MAXPATHLEN. */ static int get_pname_static(struct inode *dir, const char *name, int len, char *path, int *res_len) { char *parentname = NCP_INOP(dir)->finfo.path; int parentlen = NCP_INOP(dir)->finfo.len; #if 1 if (parentlen != strlen(parentname)) { printk("get_pname: parent->finfo.len = %d instead of %d\n", parentlen, strlen(parentname)); parentlen = strlen(parentname); } #endif DDPRINTK("get_pname_static: parentname = %s, len = %d\n", parentname, parentlen); if (len > NCP_MAXNAMELEN) { return -ENAMETOOLONG; } /* Fast cheat for . */ if (len == 0 || (len == 1 && name[0] == '.')) { memcpy(path, parentname, parentlen + 1); *res_len = parentlen; return 0; } /* Hmm, what about .. ? */ if (len == 2 && name[0] == '.' && name[1] == '.') { char *pos = strrchr(parentname, '\\'); if ( (pos == NULL) && ( (parentname[parentlen-1] == ':') || (parentlen == 0))) { /* We're at the top */ path[0] = '\0'; *res_len = 0; return 0; } if (pos == NULL) { printk("ncp_make_name: Bad parent NCP-name: %s", parentname); return -ENODATA; } len = pos - parentname; memcpy(path, parentname, len); path[len] = '\0'; } else if (parentlen == 0) { memcpy(path, name, len); path[len] = ':'; path[len+1] = '\0'; len = len+1; } else { if (len + parentlen + 2 > NCP_MAXPATHLEN) return -ENAMETOOLONG; memcpy(path, parentname, parentlen); path[parentlen] = '\\'; memcpy(path + parentlen + 1, name, len); path[parentlen + 1 + len] = '\0'; len = parentlen + len + 1; } *res_len = len; DDPRINTK("get_pname: path = %s, *pathlen = %d\n", path, *res_len); return 0; } static int get_pname(struct inode *dir, const char *name, int len, char **res_path, int *res_len) { char result[NCP_MAXPATHLEN]; int result_len; int res; if ((res = get_pname_static(dir,name,len,result,&result_len) != 0)) { return res; } if ((*res_path = ncp_kmalloc(result_len+1, GFP_KERNEL)) == NULL) { printk("get_pname: Out of memory while allocating name."); return -ENOMEM; } strcpy(*res_path, result); *res_len = result_len; return 0; } static void put_pname(char *path) { ncp_kfree_s(path, 0); } /* Insert a NEW ncp_inode_info into the inode tree of our filesystem, under dir. The caller must assure that it's not already there. We assume that path is allocated for us. */ static struct inode * ncp_iget(struct inode *dir, char *path, struct ncp_dirent *finfo) { struct inode *inode; struct ncp_inode_info *new_inode_info; struct ncp_inode_info *root; if (!dir) { printk("ncp_iget: dir is NULL\n"); return NULL; } if (!path) { printk("ncp_iget: path is NULL\n"); return NULL; } if (!finfo) { printk("ncp_iget: finfo is NULL\n"); return NULL; } new_inode_info = ncp_kmalloc(sizeof(struct ncp_inode_info), GFP_KERNEL); if (new_inode_info == NULL) { printk("ncp_iget: could not alloc mem for %s\n", path); return NULL; } new_inode_info->state = INODE_LOOKED_UP; new_inode_info->nused = 0; new_inode_info->dir = NCP_INOP(dir); new_inode_info->finfo = *finfo; new_inode_info->finfo.opened = 0; new_inode_info->finfo.path = path; new_inode_info->finfo.len = strlen(path); NCP_INOP(dir)->nused += 1; /* We have to link the new inode_info into the doubly linked list of inode_infos to make a complete linear search possible. */ root = &(NCP_SERVER(dir)->root); new_inode_info->prev = root; new_inode_info->next = root->next; root->next->prev = new_inode_info; root->next = new_inode_info; if (!(inode = iget(dir->i_sb, (int)new_inode_info))) { printk("ncp_iget: iget failed!"); return NULL; } return inode; } void ncp_free_inode_info(struct ncp_inode_info *i) { if (i == NULL) { printk("ncp_free_inode: i == NULL\n"); return; } i->state = INODE_CACHED; while ((i->nused == 0) && (i->state == INODE_CACHED)) { struct ncp_inode_info *dir = i->dir; i->next->prev = i->prev; i->prev->next = i->next; ncp_kfree_s(i->finfo.path, i->finfo.len+1); ncp_kfree_s(i, sizeof(struct ncp_inode_info)); if (dir == NULL) return; (dir->nused)--; i = dir; } } void ncp_init_root(struct ncp_server *server) { struct ncp_inode_info *root = &(server->root); DPRINTK("ncp_init_root: server %s\n", server->m.server_name); root->finfo.opened = 0; root->finfo.attr = aDIR; root->finfo.size = 1024; root->finfo.mtime = 0; root->finfo.ctime = 0; root->finfo.atime = 0; server->root_path = '\0'; root->finfo.path = &(server->root_path); root->finfo.len = 0; root->state = INODE_LOOKED_UP; root->nused = 1; root->dir = NULL; root->next = root->prev = root; return; } void ncp_free_all_inodes(struct ncp_server *server) { /* Here nothing should be to do. I do not know whether it's better to leave some memory allocated or be stuck in an endless loop */ #if 1 struct ncp_inode_info *root = &(server->root); if (root->next != root) { printk("ncp_free_all_inodes: INODES LEFT!!!\n"); } while (root->next != root) { printk("ncp_free_all_inodes: freeing inode\n"); ncp_free_inode_info(root->next); /* In case we have an endless loop.. */ schedule(); } #endif return; } /* We will search the inode that belongs to this name, currently by a complete linear search through the inodes belonging to this filesystem. This has to be fixed. */ static struct ncp_inode_info * ncp_find_inode(struct ncp_server *server, const char *path) { struct ncp_inode_info *result = &(server->root); if (path == NULL) return NULL; do { if (strcmp(result->finfo.path, path) == 0) return result; result = result->next; } while (result != &(server->root)); return NULL; } static int ncp_lookup(struct inode *dir, const char *__name, int len, struct inode **result) { char *name = NULL; struct ncp_dirent finfo; struct ncp_server *server; struct ncp_inode_info *result_info; int error; int found_in_cache; int path_len; *result = NULL; if (!dir || !S_ISDIR(dir->i_mode)) { printk("ncp_lookup: inode is NULL or not a directory.\n"); iput(dir); return -ENOENT; } DPRINTK("ncp_lookup: %s\n", __name); server = NCP_SERVER(dir); /* Fast cheat for . */ if (len == 0 || (len == 1 && __name[0] == '.')) { *result = dir; return 0; } /* Now we will have to build up an NCP filename. */ if ((error = get_pname(dir, __name, len, &name, &path_len)) < 0) { iput(dir); return error; } result_info = ncp_find_inode(NCP_SERVER(dir), name); if (result_info != 0) { if (result_info->state == INODE_CACHED) result_info->state = INODE_LOOKED_UP; put_pname(name); /* Here we convert the inode_info address into an inode number */ *result = iget(dir->i_sb, (int)result_info); iput(dir); if (*result == NULL) { return -EACCES; } return 0; } /* Ok, now we have made our name. We have to build a new ncp_inode_info struct and insert it into the tree, if it is a name that exists on the server */ /* If the file is in the dir cache, we do not have to ask the server. */ found_in_cache = 0; if (dir->i_ino == c_ino) { int first = c_last_returned_index; int i; i = first; do { DDPRINTK("ncp_lookup: trying index: %d, name: %s\n", i, c_entry[i].path); if (strcmp(c_entry[i].path, __name) == 0) { DPRINTK("ncp_lookup: found in cache!\n"); finfo = c_entry[i]; finfo.path = NULL; /* It's not ours! */ found_in_cache = 1; break; } i = (i + 1) % c_size; DDPRINTK("ncp_lookup: index %d, name %s failed\n", i, c_entry[i].path); } while (i != first); } if (found_in_cache == 0) { char this_name[len+1]; memcpy(this_name, __name, len); this_name[len] = 0; if (dir->i_ino == (int)&(server->root)) { /* We want to look up a volume. We only want to know whether it exists, nothing more. */ int vol_no; DPRINTK("ncp_lookup: looking up volume %s\n", this_name); if (ncp_get_volume_number(server,this_name, &vol_no)!=0) { put_pname(name); iput(dir); return -ENOENT; } finfo.attr = aDIR; finfo.mtime = 0; finfo.ctime = 0; finfo.atime = 0; finfo.size = 1024; } else { struct ncp_file_info ninfo; if (ncp_get_finfo(server, 0, NCP_FINFO(dir)->path, this_name, &ninfo) != 0) { error = -ENOENT; } finfo.attr = ninfo.file_attributes; finfo.mtime = date_dos2unix(ninfo.update_time, ninfo.update_date); finfo.ctime = date_dos2unix(0, ninfo.creation_date); finfo.atime = date_dos2unix(0, ninfo.access_date); finfo.size = (ninfo.file_attributes & aDIR) != 0 ? 1024 : ninfo.file_length; } if (error < 0) { put_pname(name); iput(dir); return error; } } if (!(*result = ncp_iget(dir, name, &finfo))) { put_pname(name); iput(dir); return -EACCES; } DDPRINTK("ncp_lookup: %s => %lu\n", name, (unsigned long)result_info); iput(dir); return 0; } static int ncp_create(struct inode *dir, const char *name, int len, int mode, struct inode **result) { int error; char *path = NULL; struct ncp_dirent entry; struct ncp_file_info finfo; struct ncp_dirent *ino_finfo; *result = NULL; if (!dir || !S_ISDIR(dir->i_mode)) { printk("ncp_create: inode is NULL or not a directory\n"); iput(dir); return -ENOENT; } /* Now we will have to build up an NCP filename. */ if ((error = get_pname(dir, name, len, &path, &len)) < 0) { iput(dir); return error; } if (ncp_create_newfile(NCP_SERVER(dir), 0, path, 0, &finfo) != 0) { put_pname(path); iput(dir); return -EACCES; } entry.attr = finfo.file_attributes; entry.mtime = date_dos2unix(finfo.update_time, finfo.update_date); entry.ctime = date_dos2unix(0, finfo.creation_date); entry.atime = date_dos2unix(0, finfo.access_date); entry.size = 0; ncp_invalid_dir_cache(dir->i_ino); if (!(*result = ncp_iget(dir, path, &entry)) < 0) { ncp_close_file(NCP_SERVER(dir), finfo.file_id); put_pname(path); iput(dir); return error; } ino_finfo = &(NCP_INOP(*result)->finfo); ino_finfo->opened = 1; ino_finfo->access = O_RDWR; memcpy(&(ino_finfo->file_id), finfo.file_id, NCP_FILE_ID_LEN); iput(dir); return 0; } static int ncp_mkdir(struct inode *dir, const char *name, int len, int mode) { int error; char path[NCP_MAXPATHLEN]; if (!dir || !S_ISDIR(dir->i_mode)) { printk("ncp_mkdir: inode is NULL or not a directory\n"); iput(dir); return -ENOENT; } /* Now we will have to build up an NCP filename. */ if ((error = get_pname_static(dir, name, len, path, &len)) < 0) { iput(dir); return error; } if ((error = ncp_create_directory(NCP_SERVER(dir), 0, path, 0xff)) == 0) { ncp_invalid_dir_cache(dir->i_ino); } iput(dir); return error; } static int ncp_rmdir(struct inode *dir, const char *name, int len) { int error; char path[NCP_MAXPATHLEN]; if (!dir || !S_ISDIR(dir->i_mode)) { printk("ncp_rmdir: inode is NULL or not a directory\n"); iput(dir); return -ENOENT; } if ((error = get_pname_static(dir, name, len, path, &len)) < 0) { iput(dir); return error; } if (ncp_find_inode(NCP_SERVER(dir), path) != NULL) { error = -EBUSY; } else { if ((error = ncp_delete_directory(NCP_SERVER(dir), 0, path)) == 0) { ncp_invalid_dir_cache(dir->i_ino); } } iput(dir); return error; } static int ncp_unlink(struct inode *dir, const char *name, int len) { int error; char path[NCP_MAXPATHLEN]; if (!dir || !S_ISDIR(dir->i_mode)) { printk("ncp_unlink: inode is NULL or not a directory\n"); iput(dir); return -ENOENT; } if ((error = get_pname_static(dir, name, len, path, &len)) < 0) { iput(dir); return error; } if (ncp_find_inode(NCP_SERVER(dir), path) != NULL) { error = -EBUSY; } else { if ((error = ncp_erase_file(NCP_SERVER(dir), 0, path, 0)) == 0) ncp_invalid_dir_cache(dir->i_ino); } iput(dir); return error; } static int ncp_rename(struct inode *old_dir, const char *old_name, int old_len, struct inode *new_dir, const char *new_name, int new_len) { int res; char old_path[NCP_MAXPATHLEN], new_path[NCP_MAXPATHLEN]; if (!old_dir || !S_ISDIR(old_dir->i_mode)) { printk("ncp_rename: old inode is NULL or not a directory\n"); res = -ENOENT; goto finished; } if (!new_dir || !S_ISDIR(new_dir->i_mode)) { printk("ncp_rename: new inode is NULL or not a directory\n"); res = -ENOENT; goto finished; } res = get_pname_static(old_dir, old_name, old_len, old_path, &old_len); if (res < 0) { goto finished; } res = get_pname_static(new_dir, new_name, new_len, new_path, &new_len); if (res < 0) { goto finished; } if ( (ncp_find_inode(NCP_SERVER(old_dir), old_path) != NULL) || (ncp_find_inode(NCP_SERVER(new_dir), new_path) != NULL)) { res = -EBUSY; goto finished; } res = ncp_rename_file(NCP_SERVER(old_dir), 0, old_path, 0,0, new_path); if (res == 0) { ncp_invalid_dir_cache(old_dir->i_ino); ncp_invalid_dir_cache(new_dir->i_ino); } finished: iput(old_dir); iput(new_dir); return res; } /* The following routines are taken directly from msdos-fs */ /* Linear day numbers of the respective 1sts in non-leap years. */ static int day_n[] = { 0,31,59,90,120,151,181,212,243,273,304,334,0,0,0,0 }; /* JanFebMarApr May Jun Jul Aug Sep Oct Nov Dec */ extern struct timezone sys_tz; /* static int utc2local(int time) { return time - sys_tz.tz_minuteswest*60; } */ static int local2utc(int time) { return time + sys_tz.tz_minuteswest*60; } /* Convert a MS-DOS time/date pair to a UNIX date (seconds since 1 1 70). */ static int date_dos2unix(unsigned short time,unsigned short date) { int month,year,secs; month = ((date >> 5) & 15)-1; year = date >> 9; secs = (time & 31)*2+60*((time >> 5) & 63)+(time >> 11)*3600+86400* ((date & 31)-1+day_n[month]+(year/4)+year*365-((year & 3) == 0 && month < 2 ? 1 : 0)+3653); /* days since 1.1.70 plus 80's leap day */ return local2utc(secs); } /* Convert linear UNIX date to a MS-DOS time/date pair. */ #if 0 static void date_unix2dos(int unix_date,unsigned short *time, unsigned short *date) { int day,year,nl_day,month; unix_date = utc2local(unix_date); *time = (unix_date % 60)/2+(((unix_date/60) % 60) << 5)+ (((unix_date/3600) % 24) << 11); day = unix_date/86400-3652; year = day/365; if ((year+3)/4+365*year > day) year--; day -= (year+3)/4+365*year; if (day == 59 && !(year & 3)) { nl_day = day; month = 2; } else { nl_day = (year & 3) || day <= 59 ? day : day-1; for (month = 0; month < 12; month++) if (day_n[month] > nl_day) break; } *date = nl_day-day_n[month-1]+1+(month << 5)+(year << 9); } #endif