@@ -2174,21 +2174,37 @@ EXPORT_SYMBOL(hashlen_string);
21742174 * Calculate the length and hash of the path component, and
21752175 * return the length as the result.
21762176 */
2177- static inline unsigned long hash_name (struct nameidata * nd ,
2178- const char * name ,
2179- unsigned long * lastword )
2177+ static inline const char * hash_name (struct nameidata * nd ,
2178+ const char * name ,
2179+ unsigned long * lastword )
21802180{
2181- unsigned long a = 0 , b , x = 0 , y = (unsigned long )nd -> path .dentry ;
2181+ unsigned long a , b , x , y = (unsigned long )nd -> path .dentry ;
21822182 unsigned long adata , bdata , mask , len ;
21832183 const struct word_at_a_time constants = WORD_AT_A_TIME_CONSTANTS ;
21842184
2185- len = 0 ;
2186- goto inside ;
2185+ /*
2186+ * The first iteration is special, because it can result in
2187+ * '.' and '..' and has no mixing other than the final fold.
2188+ */
2189+ a = load_unaligned_zeropad (name );
2190+ b = a ^ REPEAT_BYTE ('/' );
2191+ if (has_zero (a , & adata , & constants ) | has_zero (b , & bdata , & constants )) {
2192+ adata = prep_zero_mask (a , adata , & constants );
2193+ bdata = prep_zero_mask (b , bdata , & constants );
2194+ mask = create_zero_mask (adata | bdata );
2195+ a &= zero_bytemask (mask );
2196+ * lastword = a ;
2197+ len = find_zero (mask );
2198+ nd -> last .hash = fold_hash (a , y );
2199+ nd -> last .len = len ;
2200+ return name + len ;
2201+ }
21872202
2203+ len = 0 ;
2204+ x = 0 ;
21882205 do {
21892206 HASH_MIX (x , y , a );
21902207 len += sizeof (unsigned long );
2191- inside :
21922208 a = load_unaligned_zeropad (name + len );
21932209 b = a ^ REPEAT_BYTE ('/' );
21942210 } while (!(has_zero (a , & adata , & constants ) | has_zero (b , & bdata , & constants )));
@@ -2197,13 +2213,13 @@ static inline unsigned long hash_name(struct nameidata *nd,
21972213 bdata = prep_zero_mask (b , bdata , & constants );
21982214 mask = create_zero_mask (adata | bdata );
21992215 a &= zero_bytemask (mask );
2200- * lastword = a ;
22012216 x ^= a ;
22022217 len += find_zero (mask );
2218+ * lastword = 0 ; // Multi-word components cannot be DOT or DOTDOT
22032219
22042220 nd -> last .hash = fold_hash (x , y );
22052221 nd -> last .len = len ;
2206- return len ;
2222+ return name + len ;
22072223}
22082224
22092225/*
@@ -2247,7 +2263,7 @@ EXPORT_SYMBOL(hashlen_string);
22472263 * We know there's a real path component here of at least
22482264 * one character.
22492265 */
2250- static inline unsigned long hash_name (struct nameidata * nd , const char * name , unsigned long * lastword )
2266+ static inline const char * hash_name (struct nameidata * nd , const char * name , unsigned long * lastword )
22512267{
22522268 unsigned long hash = init_name_hash (nd -> path .dentry );
22532269 unsigned long len = 0 , c , last = 0 ;
@@ -2259,10 +2275,14 @@ static inline unsigned long hash_name(struct nameidata *nd, const char *name, un
22592275 hash = partial_name_hash (c , hash );
22602276 c = (unsigned char )name [len ];
22612277 } while (c && c != '/' );
2278+
2279+ // This is reliable for DOT or DOTDOT, since the component
2280+ // cannot contain NUL characters - top bits being zero means
2281+ // we cannot have had any other pathnames.
22622282 * lastword = last ;
22632283 nd -> last .hash = end_name_hash (hash );
22642284 nd -> last .len = len ;
2265- return len ;
2285+ return name + len ;
22662286}
22672287
22682288#endif
@@ -2301,33 +2321,26 @@ static int link_path_walk(const char *name, struct nameidata *nd)
23012321 struct mnt_idmap * idmap ;
23022322 const char * link ;
23032323 unsigned long lastword ;
2304- unsigned int len ;
23052324
23062325 idmap = mnt_idmap (nd -> path .mnt );
23072326 err = may_lookup (idmap , nd );
23082327 if (err )
23092328 return err ;
23102329
23112330 nd -> last .name = name ;
2312- len = hash_name (nd , name , & lastword );
2313- name += len ;
2331+ name = hash_name (nd , name , & lastword );
23142332
23152333 switch (lastword ) {
23162334 case LAST_WORD_IS_DOTDOT :
2317- if (len != 2 )
2318- goto normal ;
23192335 nd -> last_type = LAST_DOTDOT ;
23202336 nd -> state |= ND_JUMPED ;
23212337 break ;
23222338
23232339 case LAST_WORD_IS_DOT :
2324- if (len != 1 )
2325- goto normal ;
23262340 nd -> last_type = LAST_DOT ;
23272341 break ;
23282342
23292343 default :
2330- normal :
23312344 nd -> last_type = LAST_NORM ;
23322345 nd -> state &= ~ND_JUMPED ;
23332346
0 commit comments