~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/fs/hfsplus/dir.c

Version: ~ [ linux-5.14-rc3 ] ~ [ linux-5.13.5 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.53 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.135 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.198 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.240 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.276 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.276 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.18.140 ] ~ [ linux-3.16.85 ] ~ [ linux-3.14.79 ] ~ [ linux-3.12.74 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.5 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 /*
  2  *  linux/fs/hfsplus/dir.c
  3  *
  4  * Copyright (C) 2001
  5  * Brad Boyer (flar@allandria.com)
  6  * (C) 2003 Ardis Technologies <roman@ardistech.com>
  7  *
  8  * Handling of directories
  9  */
 10 
 11 #include <linux/errno.h>
 12 #include <linux/fs.h>
 13 #include <linux/slab.h>
 14 #include <linux/random.h>
 15 
 16 #include "hfsplus_fs.h"
 17 #include "hfsplus_raw.h"
 18 #include "xattr.h"
 19 #include "acl.h"
 20 
 21 static inline void hfsplus_instantiate(struct dentry *dentry,
 22                                        struct inode *inode, u32 cnid)
 23 {
 24         dentry->d_fsdata = (void *)(unsigned long)cnid;
 25         d_instantiate(dentry, inode);
 26 }
 27 
 28 /* Find the entry inside dir named dentry->d_name */
 29 static struct dentry *hfsplus_lookup(struct inode *dir, struct dentry *dentry,
 30                                      unsigned int flags)
 31 {
 32         struct inode *inode = NULL;
 33         struct hfs_find_data fd;
 34         struct super_block *sb;
 35         hfsplus_cat_entry entry;
 36         int err;
 37         u32 cnid, linkid = 0;
 38         u16 type;
 39 
 40         sb = dir->i_sb;
 41 
 42         dentry->d_fsdata = NULL;
 43         err = hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &fd);
 44         if (err)
 45                 return ERR_PTR(err);
 46         hfsplus_cat_build_key(sb, fd.search_key, dir->i_ino, &dentry->d_name);
 47 again:
 48         err = hfs_brec_read(&fd, &entry, sizeof(entry));
 49         if (err) {
 50                 if (err == -ENOENT) {
 51                         hfs_find_exit(&fd);
 52                         /* No such entry */
 53                         inode = NULL;
 54                         goto out;
 55                 }
 56                 goto fail;
 57         }
 58         type = be16_to_cpu(entry.type);
 59         if (type == HFSPLUS_FOLDER) {
 60                 if (fd.entrylength < sizeof(struct hfsplus_cat_folder)) {
 61                         err = -EIO;
 62                         goto fail;
 63                 }
 64                 cnid = be32_to_cpu(entry.folder.id);
 65                 dentry->d_fsdata = (void *)(unsigned long)cnid;
 66         } else if (type == HFSPLUS_FILE) {
 67                 if (fd.entrylength < sizeof(struct hfsplus_cat_file)) {
 68                         err = -EIO;
 69                         goto fail;
 70                 }
 71                 cnid = be32_to_cpu(entry.file.id);
 72                 if (entry.file.user_info.fdType ==
 73                                 cpu_to_be32(HFSP_HARDLINK_TYPE) &&
 74                                 entry.file.user_info.fdCreator ==
 75                                 cpu_to_be32(HFSP_HFSPLUS_CREATOR) &&
 76                                 (entry.file.create_date ==
 77                                         HFSPLUS_I(HFSPLUS_SB(sb)->hidden_dir)->
 78                                                 create_date ||
 79                                 entry.file.create_date ==
 80                                         HFSPLUS_I(sb->s_root->d_inode)->
 81                                                 create_date) &&
 82                                 HFSPLUS_SB(sb)->hidden_dir) {
 83                         struct qstr str;
 84                         char name[32];
 85 
 86                         if (dentry->d_fsdata) {
 87                                 /*
 88                                  * We found a link pointing to another link,
 89                                  * so ignore it and treat it as regular file.
 90                                  */
 91                                 cnid = (unsigned long)dentry->d_fsdata;
 92                                 linkid = 0;
 93                         } else {
 94                                 dentry->d_fsdata = (void *)(unsigned long)cnid;
 95                                 linkid =
 96                                         be32_to_cpu(entry.file.permissions.dev);
 97                                 str.len = sprintf(name, "iNode%d", linkid);
 98                                 str.name = name;
 99                                 hfsplus_cat_build_key(sb, fd.search_key,
100                                         HFSPLUS_SB(sb)->hidden_dir->i_ino,
101                                         &str);
102                                 goto again;
103                         }
104                 } else if (!dentry->d_fsdata)
105                         dentry->d_fsdata = (void *)(unsigned long)cnid;
106         } else {
107                 pr_err("invalid catalog entry type in lookup\n");
108                 err = -EIO;
109                 goto fail;
110         }
111         hfs_find_exit(&fd);
112         inode = hfsplus_iget(dir->i_sb, cnid);
113         if (IS_ERR(inode))
114                 return ERR_CAST(inode);
115         if (S_ISREG(inode->i_mode))
116                 HFSPLUS_I(inode)->linkid = linkid;
117 out:
118         d_add(dentry, inode);
119         return NULL;
120 fail:
121         hfs_find_exit(&fd);
122         return ERR_PTR(err);
123 }
124 
125 static int hfsplus_readdir(struct file *file, struct dir_context *ctx)
126 {
127         struct inode *inode = file_inode(file);
128         struct super_block *sb = inode->i_sb;
129         int len, err;
130         char strbuf[HFSPLUS_MAX_STRLEN + 1];
131         hfsplus_cat_entry entry;
132         struct hfs_find_data fd;
133         struct hfsplus_readdir_data *rd;
134         u16 type;
135 
136         if (file->f_pos >= inode->i_size)
137                 return 0;
138 
139         err = hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &fd);
140         if (err)
141                 return err;
142         hfsplus_cat_build_key(sb, fd.search_key, inode->i_ino, NULL);
143         err = hfs_brec_find(&fd, hfs_find_rec_by_key);
144         if (err)
145                 goto out;
146 
147         if (ctx->pos == 0) {
148                 /* This is completely artificial... */
149                 if (!dir_emit_dot(file, ctx))
150                         goto out;
151                 ctx->pos = 1;
152         }
153         if (ctx->pos == 1) {
154                 if (fd.entrylength > sizeof(entry) || fd.entrylength < 0) {
155                         err = -EIO;
156                         goto out;
157                 }
158 
159                 hfs_bnode_read(fd.bnode, &entry, fd.entryoffset,
160                         fd.entrylength);
161                 if (be16_to_cpu(entry.type) != HFSPLUS_FOLDER_THREAD) {
162                         pr_err("bad catalog folder thread\n");
163                         err = -EIO;
164                         goto out;
165                 }
166                 if (fd.entrylength < HFSPLUS_MIN_THREAD_SZ) {
167                         pr_err("truncated catalog thread\n");
168                         err = -EIO;
169                         goto out;
170                 }
171                 if (!dir_emit(ctx, "..", 2,
172                             be32_to_cpu(entry.thread.parentID), DT_DIR))
173                         goto out;
174                 ctx->pos = 2;
175         }
176         if (ctx->pos >= inode->i_size)
177                 goto out;
178         err = hfs_brec_goto(&fd, ctx->pos - 1);
179         if (err)
180                 goto out;
181         for (;;) {
182                 if (be32_to_cpu(fd.key->cat.parent) != inode->i_ino) {
183                         pr_err("walked past end of dir\n");
184                         err = -EIO;
185                         goto out;
186                 }
187 
188                 if (fd.entrylength > sizeof(entry) || fd.entrylength < 0) {
189                         err = -EIO;
190                         goto out;
191                 }
192 
193                 hfs_bnode_read(fd.bnode, &entry, fd.entryoffset,
194                         fd.entrylength);
195                 type = be16_to_cpu(entry.type);
196                 len = HFSPLUS_MAX_STRLEN;
197                 err = hfsplus_uni2asc(sb, &fd.key->cat.name, strbuf, &len);
198                 if (err)
199                         goto out;
200                 if (type == HFSPLUS_FOLDER) {
201                         if (fd.entrylength <
202                                         sizeof(struct hfsplus_cat_folder)) {
203                                 pr_err("small dir entry\n");
204                                 err = -EIO;
205                                 goto out;
206                         }
207                         if (HFSPLUS_SB(sb)->hidden_dir &&
208                             HFSPLUS_SB(sb)->hidden_dir->i_ino ==
209                                         be32_to_cpu(entry.folder.id))
210                                 goto next;
211                         if (!dir_emit(ctx, strbuf, len,
212                                     be32_to_cpu(entry.folder.id), DT_DIR))
213                                 break;
214                 } else if (type == HFSPLUS_FILE) {
215                         u16 mode;
216                         unsigned type = DT_UNKNOWN;
217 
218                         if (fd.entrylength < sizeof(struct hfsplus_cat_file)) {
219                                 pr_err("small file entry\n");
220                                 err = -EIO;
221                                 goto out;
222                         }
223 
224                         mode = be16_to_cpu(entry.file.permissions.mode);
225                         if (S_ISREG(mode))
226                                 type = DT_REG;
227                         else if (S_ISLNK(mode))
228                                 type = DT_LNK;
229                         else if (S_ISFIFO(mode))
230                                 type = DT_FIFO;
231                         else if (S_ISCHR(mode))
232                                 type = DT_CHR;
233                         else if (S_ISBLK(mode))
234                                 type = DT_BLK;
235                         else if (S_ISSOCK(mode))
236                                 type = DT_SOCK;
237 
238                         if (!dir_emit(ctx, strbuf, len,
239                                       be32_to_cpu(entry.file.id), type))
240                                 break;
241                 } else {
242                         pr_err("bad catalog entry type\n");
243                         err = -EIO;
244                         goto out;
245                 }
246 next:
247                 ctx->pos++;
248                 if (ctx->pos >= inode->i_size)
249                         goto out;
250                 err = hfs_brec_goto(&fd, 1);
251                 if (err)
252                         goto out;
253         }
254         rd = file->private_data;
255         if (!rd) {
256                 rd = kmalloc(sizeof(struct hfsplus_readdir_data), GFP_KERNEL);
257                 if (!rd) {
258                         err = -ENOMEM;
259                         goto out;
260                 }
261                 file->private_data = rd;
262                 rd->file = file;
263                 list_add(&rd->list, &HFSPLUS_I(inode)->open_dir_list);
264         }
265         memcpy(&rd->key, fd.key, sizeof(struct hfsplus_cat_key));
266 out:
267         hfs_find_exit(&fd);
268         return err;
269 }
270 
271 static int hfsplus_dir_release(struct inode *inode, struct file *file)
272 {
273         struct hfsplus_readdir_data *rd = file->private_data;
274         if (rd) {
275                 mutex_lock(&inode->i_mutex);
276                 list_del(&rd->list);
277                 mutex_unlock(&inode->i_mutex);
278                 kfree(rd);
279         }
280         return 0;
281 }
282 
283 static int hfsplus_link(struct dentry *src_dentry, struct inode *dst_dir,
284                         struct dentry *dst_dentry)
285 {
286         struct hfsplus_sb_info *sbi = HFSPLUS_SB(dst_dir->i_sb);
287         struct inode *inode = src_dentry->d_inode;
288         struct inode *src_dir = src_dentry->d_parent->d_inode;
289         struct qstr str;
290         char name[32];
291         u32 cnid, id;
292         int res;
293 
294         if (HFSPLUS_IS_RSRC(inode))
295                 return -EPERM;
296         if (!S_ISREG(inode->i_mode))
297                 return -EPERM;
298 
299         mutex_lock(&sbi->vh_mutex);
300         if (inode->i_ino == (u32)(unsigned long)src_dentry->d_fsdata) {
301                 for (;;) {
302                         get_random_bytes(&id, sizeof(cnid));
303                         id &= 0x3fffffff;
304                         str.name = name;
305                         str.len = sprintf(name, "iNode%d", id);
306                         res = hfsplus_rename_cat(inode->i_ino,
307                                                  src_dir, &src_dentry->d_name,
308                                                  sbi->hidden_dir, &str);
309                         if (!res)
310                                 break;
311                         if (res != -EEXIST)
312                                 goto out;
313                 }
314                 HFSPLUS_I(inode)->linkid = id;
315                 cnid = sbi->next_cnid++;
316                 src_dentry->d_fsdata = (void *)(unsigned long)cnid;
317                 res = hfsplus_create_cat(cnid, src_dir,
318                         &src_dentry->d_name, inode);
319                 if (res)
320                         /* panic? */
321                         goto out;
322                 sbi->file_count++;
323         }
324         cnid = sbi->next_cnid++;
325         res = hfsplus_create_cat(cnid, dst_dir, &dst_dentry->d_name, inode);
326         if (res)
327                 goto out;
328 
329         inc_nlink(inode);
330         hfsplus_instantiate(dst_dentry, inode, cnid);
331         ihold(inode);
332         inode->i_ctime = CURRENT_TIME_SEC;
333         mark_inode_dirty(inode);
334         sbi->file_count++;
335         hfsplus_mark_mdb_dirty(dst_dir->i_sb);
336 out:
337         mutex_unlock(&sbi->vh_mutex);
338         return res;
339 }
340 
341 static int hfsplus_unlink(struct inode *dir, struct dentry *dentry)
342 {
343         struct hfsplus_sb_info *sbi = HFSPLUS_SB(dir->i_sb);
344         struct inode *inode = dentry->d_inode;
345         struct qstr str;
346         char name[32];
347         u32 cnid;
348         int res;
349 
350         if (HFSPLUS_IS_RSRC(inode))
351                 return -EPERM;
352 
353         mutex_lock(&sbi->vh_mutex);
354         cnid = (u32)(unsigned long)dentry->d_fsdata;
355         if (inode->i_ino == cnid &&
356             atomic_read(&HFSPLUS_I(inode)->opencnt)) {
357                 str.name = name;
358                 str.len = sprintf(name, "temp%lu", inode->i_ino);
359                 res = hfsplus_rename_cat(inode->i_ino,
360                                          dir, &dentry->d_name,
361                                          sbi->hidden_dir, &str);
362                 if (!res) {
363                         inode->i_flags |= S_DEAD;
364                         drop_nlink(inode);
365                 }
366                 goto out;
367         }
368         res = hfsplus_delete_cat(cnid, dir, &dentry->d_name);
369         if (res)
370                 goto out;
371 
372         if (inode->i_nlink > 0)
373                 drop_nlink(inode);
374         if (inode->i_ino == cnid)
375                 clear_nlink(inode);
376         if (!inode->i_nlink) {
377                 if (inode->i_ino != cnid) {
378                         sbi->file_count--;
379                         if (!atomic_read(&HFSPLUS_I(inode)->opencnt)) {
380                                 res = hfsplus_delete_cat(inode->i_ino,
381                                                          sbi->hidden_dir,
382                                                          NULL);
383                                 if (!res)
384                                         hfsplus_delete_inode(inode);
385                         } else
386                                 inode->i_flags |= S_DEAD;
387                 } else
388                         hfsplus_delete_inode(inode);
389         } else
390                 sbi->file_count--;
391         inode->i_ctime = CURRENT_TIME_SEC;
392         mark_inode_dirty(inode);
393 out:
394         mutex_unlock(&sbi->vh_mutex);
395         return res;
396 }
397 
398 static int hfsplus_rmdir(struct inode *dir, struct dentry *dentry)
399 {
400         struct hfsplus_sb_info *sbi = HFSPLUS_SB(dir->i_sb);
401         struct inode *inode = dentry->d_inode;
402         int res;
403 
404         if (inode->i_size != 2)
405                 return -ENOTEMPTY;
406 
407         mutex_lock(&sbi->vh_mutex);
408         res = hfsplus_delete_cat(inode->i_ino, dir, &dentry->d_name);
409         if (res)
410                 goto out;
411         clear_nlink(inode);
412         inode->i_ctime = CURRENT_TIME_SEC;
413         hfsplus_delete_inode(inode);
414         mark_inode_dirty(inode);
415 out:
416         mutex_unlock(&sbi->vh_mutex);
417         return res;
418 }
419 
420 static int hfsplus_symlink(struct inode *dir, struct dentry *dentry,
421                            const char *symname)
422 {
423         struct hfsplus_sb_info *sbi = HFSPLUS_SB(dir->i_sb);
424         struct inode *inode;
425         int res = -ENOSPC;
426 
427         mutex_lock(&sbi->vh_mutex);
428         inode = hfsplus_new_inode(dir->i_sb, S_IFLNK | S_IRWXUGO);
429         if (!inode)
430                 goto out;
431 
432         res = page_symlink(inode, symname, strlen(symname) + 1);
433         if (res)
434                 goto out_err;
435 
436         res = hfsplus_create_cat(inode->i_ino, dir, &dentry->d_name, inode);
437         if (res)
438                 goto out_err;
439 
440         res = hfsplus_init_inode_security(inode, dir, &dentry->d_name);
441         if (res == -EOPNOTSUPP)
442                 res = 0; /* Operation is not supported. */
443         else if (res) {
444                 /* Try to delete anyway without error analysis. */
445                 hfsplus_delete_cat(inode->i_ino, dir, &dentry->d_name);
446                 goto out_err;
447         }
448 
449         hfsplus_instantiate(dentry, inode, inode->i_ino);
450         mark_inode_dirty(inode);
451         goto out;
452 
453 out_err:
454         clear_nlink(inode);
455         hfsplus_delete_inode(inode);
456         iput(inode);
457 out:
458         mutex_unlock(&sbi->vh_mutex);
459         return res;
460 }
461 
462 static int hfsplus_mknod(struct inode *dir, struct dentry *dentry,
463                          umode_t mode, dev_t rdev)
464 {
465         struct hfsplus_sb_info *sbi = HFSPLUS_SB(dir->i_sb);
466         struct inode *inode;
467         int res = -ENOSPC;
468 
469         mutex_lock(&sbi->vh_mutex);
470         inode = hfsplus_new_inode(dir->i_sb, mode);
471         if (!inode)
472                 goto out;
473 
474         if (S_ISBLK(mode) || S_ISCHR(mode) || S_ISFIFO(mode) || S_ISSOCK(mode))
475                 init_special_inode(inode, mode, rdev);
476 
477         res = hfsplus_create_cat(inode->i_ino, dir, &dentry->d_name, inode);
478         if (res)
479                 goto failed_mknod;
480 
481         res = hfsplus_init_inode_security(inode, dir, &dentry->d_name);
482         if (res == -EOPNOTSUPP)
483                 res = 0; /* Operation is not supported. */
484         else if (res) {
485                 /* Try to delete anyway without error analysis. */
486                 hfsplus_delete_cat(inode->i_ino, dir, &dentry->d_name);
487                 goto failed_mknod;
488         }
489 
490         hfsplus_instantiate(dentry, inode, inode->i_ino);
491         mark_inode_dirty(inode);
492         goto out;
493 
494 failed_mknod:
495         clear_nlink(inode);
496         hfsplus_delete_inode(inode);
497         iput(inode);
498 out:
499         mutex_unlock(&sbi->vh_mutex);
500         return res;
501 }
502 
503 static int hfsplus_create(struct inode *dir, struct dentry *dentry, umode_t mode,
504                           bool excl)
505 {
506         return hfsplus_mknod(dir, dentry, mode, 0);
507 }
508 
509 static int hfsplus_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
510 {
511         return hfsplus_mknod(dir, dentry, mode | S_IFDIR, 0);
512 }
513 
514 static int hfsplus_rename(struct inode *old_dir, struct dentry *old_dentry,
515                           struct inode *new_dir, struct dentry *new_dentry)
516 {
517         int res;
518 
519         /* Unlink destination if it already exists */
520         if (new_dentry->d_inode) {
521                 if (S_ISDIR(new_dentry->d_inode->i_mode))
522                         res = hfsplus_rmdir(new_dir, new_dentry);
523                 else
524                         res = hfsplus_unlink(new_dir, new_dentry);
525                 if (res)
526                         return res;
527         }
528 
529         res = hfsplus_rename_cat((u32)(unsigned long)old_dentry->d_fsdata,
530                                  old_dir, &old_dentry->d_name,
531                                  new_dir, &new_dentry->d_name);
532         if (!res)
533                 new_dentry->d_fsdata = old_dentry->d_fsdata;
534         return res;
535 }
536 
537 const struct inode_operations hfsplus_dir_inode_operations = {
538         .lookup                 = hfsplus_lookup,
539         .create                 = hfsplus_create,
540         .link                   = hfsplus_link,
541         .unlink                 = hfsplus_unlink,
542         .mkdir                  = hfsplus_mkdir,
543         .rmdir                  = hfsplus_rmdir,
544         .symlink                = hfsplus_symlink,
545         .mknod                  = hfsplus_mknod,
546         .rename                 = hfsplus_rename,
547         .setxattr               = generic_setxattr,
548         .getxattr               = generic_getxattr,
549         .listxattr              = hfsplus_listxattr,
550         .removexattr            = hfsplus_removexattr,
551 #ifdef CONFIG_HFSPLUS_FS_POSIX_ACL
552         .get_acl                = hfsplus_get_posix_acl,
553 #endif
554 };
555 
556 const struct file_operations hfsplus_dir_operations = {
557         .fsync          = hfsplus_file_fsync,
558         .read           = generic_read_dir,
559         .iterate        = hfsplus_readdir,
560         .unlocked_ioctl = hfsplus_ioctl,
561         .llseek         = generic_file_llseek,
562         .release        = hfsplus_dir_release,
563 };
564 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

osdn.jp